rubocop 1.36.0 → 1.40.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +78 -12
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop/arguments_env.rb +17 -0
  6. data/lib/rubocop/arguments_file.rb +17 -0
  7. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  8. data/lib/rubocop/cli/command/suggest_extensions.rb +8 -1
  9. data/lib/rubocop/comment_config.rb +41 -1
  10. data/lib/rubocop/config.rb +5 -4
  11. data/lib/rubocop/config_loader.rb +5 -5
  12. data/lib/rubocop/config_loader_resolver.rb +1 -1
  13. data/lib/rubocop/cop/base.rb +2 -9
  14. data/lib/rubocop/cop/commissioner.rb +3 -1
  15. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  16. data/lib/rubocop/cop/generator.rb +1 -2
  17. data/lib/rubocop/cop/internal_affairs/create_empty_file.rb +37 -0
  18. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +111 -0
  19. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  20. data/lib/rubocop/cop/internal_affairs.rb +3 -0
  21. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  22. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  23. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +29 -8
  25. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  26. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  27. data/lib/rubocop/cop/layout/space_inside_array_percent_literal.rb +3 -0
  28. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +30 -3
  29. data/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb +34 -0
  30. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +6 -2
  31. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -2
  32. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  33. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  34. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  35. data/lib/rubocop/cop/lint/duplicate_methods.rb +28 -9
  36. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  37. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  38. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  39. data/lib/rubocop/cop/lint/empty_conditional_body.rb +21 -9
  40. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  41. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  42. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  43. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  44. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  45. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  47. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +36 -4
  48. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  49. data/lib/rubocop/cop/lint/redundant_require_statement.rb +38 -10
  50. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  51. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -8
  52. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  53. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  54. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -3
  55. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  56. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  57. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  58. data/lib/rubocop/cop/lint/void.rb +6 -6
  59. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  60. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  61. data/lib/rubocop/cop/metrics/class_length.rb +9 -4
  62. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  63. data/lib/rubocop/cop/metrics/module_length.rb +9 -4
  64. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -2
  65. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  66. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  67. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +30 -8
  68. data/lib/rubocop/cop/mixin/range_help.rb +23 -0
  69. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  70. data/lib/rubocop/cop/mixin/statement_modifier.rb +15 -1
  71. data/lib/rubocop/cop/mixin/surrounding_space.rb +10 -8
  72. data/lib/rubocop/cop/mixin/visibility_help.rb +40 -5
  73. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  74. data/lib/rubocop/cop/registry.rb +32 -14
  75. data/lib/rubocop/cop/style/access_modifier_declarations.rb +5 -7
  76. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  77. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  78. data/lib/rubocop/cop/style/block_delimiters.rb +2 -2
  79. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  80. data/lib/rubocop/cop/style/class_equality_comparison.rb +8 -6
  81. data/lib/rubocop/cop/style/collection_compact.rb +12 -3
  82. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  83. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  84. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  85. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  86. data/lib/rubocop/cop/style/guard_clause.rb +90 -22
  87. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -0
  88. data/lib/rubocop/cop/style/hash_each_methods.rb +32 -10
  89. data/lib/rubocop/cop/style/hash_except.rb +4 -0
  90. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  91. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +25 -2
  92. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -1
  93. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  94. data/lib/rubocop/cop/style/module_function.rb +28 -6
  95. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  96. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  97. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  98. data/lib/rubocop/cop/style/object_then.rb +3 -0
  99. data/lib/rubocop/cop/style/operator_method_call.rb +53 -0
  100. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  101. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  102. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  103. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  104. data/lib/rubocop/cop/style/redundant_constant_base.rb +72 -0
  105. data/lib/rubocop/cop/style/redundant_each.rb +116 -0
  106. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  107. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  108. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +12 -3
  109. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  110. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  111. data/lib/rubocop/cop/style/redundant_string_escape.rb +181 -0
  112. data/lib/rubocop/cop/style/require_order.rb +88 -0
  113. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  114. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  115. data/lib/rubocop/cop/style/select_by_regexp.rb +8 -4
  116. data/lib/rubocop/cop/style/static_class.rb +32 -1
  117. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  118. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  119. data/lib/rubocop/cop/style/symbol_proc.rb +3 -5
  120. data/lib/rubocop/cop/style/word_array.rb +2 -0
  121. data/lib/rubocop/cop/team.rb +4 -5
  122. data/lib/rubocop/cop/util.rb +2 -2
  123. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  124. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  125. data/lib/rubocop/cop/variable_force.rb +20 -29
  126. data/lib/rubocop/cops_documentation_generator.rb +2 -1
  127. data/lib/rubocop/ext/processed_source.rb +2 -0
  128. data/lib/rubocop/formatter/disabled_config_formatter.rb +25 -8
  129. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  130. data/lib/rubocop/formatter/offense_count_formatter.rb +8 -5
  131. data/lib/rubocop/formatter/worst_offenders_formatter.rb +6 -3
  132. data/lib/rubocop/formatter.rb +3 -1
  133. data/lib/rubocop/optimized_patterns.rb +38 -0
  134. data/lib/rubocop/options.rb +28 -16
  135. data/lib/rubocop/path_util.rb +14 -2
  136. data/lib/rubocop/result_cache.rb +1 -1
  137. data/lib/rubocop/rspec/cop_helper.rb +24 -1
  138. data/lib/rubocop/rspec/shared_contexts.rb +14 -1
  139. data/lib/rubocop/rspec/support.rb +2 -2
  140. data/lib/rubocop/runner.rb +15 -11
  141. data/lib/rubocop/server/cache.rb +5 -1
  142. data/lib/rubocop/server/cli.rb +9 -2
  143. data/lib/rubocop/server/client_command/exec.rb +5 -0
  144. data/lib/rubocop/server/core.rb +19 -2
  145. data/lib/rubocop/server/socket_reader.rb +5 -1
  146. data/lib/rubocop/server.rb +1 -1
  147. data/lib/rubocop/target_ruby.rb +1 -1
  148. data/lib/rubocop/version.rb +8 -3
  149. data/lib/rubocop.rb +18 -6
  150. metadata +18 -5
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Use `RUBY` for heredoc delimiter of example Ruby code.
7
+ #
8
+ # Some editors may apply better syntax highlighting by using appropriate language names for
9
+ # the delimiter.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # expect_offense(<<~CODE)
14
+ # example_ruby_code
15
+ # CODE
16
+ #
17
+ # # good
18
+ # expect_offense(<<~RUBY)
19
+ # example_ruby_code
20
+ # RUBY
21
+ class ExampleHeredocDelimiter < Base
22
+ extend AutoCorrector
23
+
24
+ EXPECTED_HEREDOC_DELIMITER = 'RUBY'
25
+
26
+ MSG = 'Use `RUBY` for heredoc delimiter of example Ruby code.'
27
+
28
+ RESTRICT_ON_SEND = %i[
29
+ expect_correction
30
+ expect_no_corrections
31
+ expect_no_offenses
32
+ expect_offense
33
+ ].freeze
34
+
35
+ # @param node [RuboCop::AST::SendNode]
36
+ # @return [void]
37
+ def on_send(node)
38
+ heredoc_node = heredoc_node_from(node)
39
+ return unless heredoc_node
40
+ return if expected_heredoc_delimiter?(heredoc_node)
41
+ return if expected_heredoc_delimiter_in_body?(heredoc_node)
42
+
43
+ add_offense(heredoc_node) do |corrector|
44
+ autocorrect(corrector, heredoc_node)
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ # @param corrector [RuboCop::Cop::Corrector]
51
+ # @param node [RuboCop::AST::StrNode]
52
+ # @return [void]
53
+ def autocorrect(corrector, node)
54
+ [
55
+ heredoc_openning_delimiter_range_from(node),
56
+ heredoc_closing_delimiter_range_from(node)
57
+ ].each do |range|
58
+ corrector.replace(range, EXPECTED_HEREDOC_DELIMITER)
59
+ end
60
+ end
61
+
62
+ # @param node [RuboCop::AST::StrNode]
63
+ # @return [Boolean]
64
+ def expected_heredoc_delimiter_in_body?(node)
65
+ node.location.heredoc_body.source.lines.any? do |line|
66
+ line.strip == EXPECTED_HEREDOC_DELIMITER
67
+ end
68
+ end
69
+
70
+ # @param node [RuboCop::AST::StrNode]
71
+ # @return [Boolean]
72
+ def expected_heredoc_delimiter?(node)
73
+ heredoc_delimiter_string_from(node) == EXPECTED_HEREDOC_DELIMITER
74
+ end
75
+
76
+ # @param node [RuboCop::AST::SendNode]
77
+ # @return [RuboCop::AST::StrNode, nil]
78
+ def heredoc_node_from(node)
79
+ return unless node.first_argument.respond_to?(:heredoc?)
80
+ return unless node.first_argument.heredoc?
81
+
82
+ node.first_argument
83
+ end
84
+
85
+ # @param node [RuboCop::AST::StrNode]
86
+ # @return [String]
87
+ def heredoc_delimiter_string_from(node)
88
+ node.source[Heredoc::OPENING_DELIMITER, 2]
89
+ end
90
+
91
+ # @param node [RuboCop::AST::StrNode]
92
+ # @return [Parser::Source::Range]
93
+ def heredoc_openning_delimiter_range_from(node)
94
+ match_data = node.source.match(Heredoc::OPENING_DELIMITER)
95
+ node.location.expression.begin.adjust(
96
+ begin_pos: match_data.begin(2),
97
+ end_pos: match_data.end(2)
98
+ )
99
+ end
100
+
101
+ # @param node [RuboCop::AST::StrNode]
102
+ # @return [Parser::Source::Range]
103
+ def heredoc_closing_delimiter_range_from(node)
104
+ node.location.heredoc_end.end.adjust(
105
+ begin_pos: -heredoc_delimiter_string_from(node).length
106
+ )
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Enforces the use of `node.lambda_or_proc?` instead of `node.lambda? || node.proc?`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # node.lambda? || node.proc?
11
+ # node.proc? || node.lambda?
12
+ #
13
+ # # good
14
+ # node.lambda_or_proc?
15
+ #
16
+ class LambdaOrProc < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Use `%<prefer>s`.'
20
+
21
+ # @!method lambda_or_proc(node)
22
+ def_node_matcher :lambda_or_proc, <<~PATTERN
23
+ {
24
+ (or $(send _node :lambda?) $(send _node :proc?))
25
+ (or $(send _node :proc?) $(send _node :lambda?))
26
+ (or
27
+ (or _ $(send _node :lambda?)) $(send _node :proc?))
28
+ (or
29
+ (or _ $(send _node :proc?)) $(send _node :lambda?))
30
+ }
31
+ PATTERN
32
+
33
+ def on_or(node)
34
+ return unless (lhs, rhs = lambda_or_proc(node))
35
+
36
+ offense = lhs.receiver.source_range.join(rhs.source_range.end)
37
+ prefer = "#{lhs.receiver.source}.lambda_or_proc?"
38
+
39
+ add_offense(offense, message: format(MSG, prefer: prefer)) do |corrector|
40
+ corrector.replace(offense, prefer)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'internal_affairs/cop_description'
4
+ require_relative 'internal_affairs/create_empty_file'
4
5
  require_relative 'internal_affairs/empty_line_between_expect_offense_and_correction'
5
6
  require_relative 'internal_affairs/example_description'
7
+ require_relative 'internal_affairs/example_heredoc_delimiter'
6
8
  require_relative 'internal_affairs/inherit_deprecated_cop_class'
9
+ require_relative 'internal_affairs/lambda_or_proc'
7
10
  require_relative 'internal_affairs/location_line_equality_comparison'
8
11
  require_relative 'internal_affairs/method_name_end_with'
9
12
  require_relative 'internal_affairs/method_name_equal'
@@ -162,6 +162,7 @@ module RuboCop
162
162
  check_alignment([node.first_argument], indent)
163
163
  end
164
164
  alias on_csend on_send
165
+ alias on_super on_send
165
166
 
166
167
  private
167
168
 
@@ -90,7 +90,7 @@ module RuboCop
90
90
  # which lines start inside a string literal?
91
91
  return [] if ast.nil?
92
92
 
93
- ast.each_node(:str, :dstr).each_with_object(Set.new) do |str, ranges|
93
+ ast.each_node(:str, :dstr).with_object(Set.new) do |str, ranges|
94
94
  loc = str.location
95
95
 
96
96
  if str.heredoc?
@@ -343,7 +343,7 @@ module RuboCop
343
343
  end
344
344
 
345
345
  def skip_check?(base_loc, body_node)
346
- return true if ignored_line?(base_loc)
346
+ return true if allowed_line?(base_loc)
347
347
  return true unless body_node
348
348
 
349
349
  # Don't check if expression is on same line as "then" keyword, etc.
@@ -42,6 +42,14 @@ module RuboCop
42
42
  # ' long'
43
43
  class LineContinuationLeadingSpace < Base
44
44
  include RangeHelp
45
+ extend AutoCorrector
46
+
47
+ LINE_1_ENDING = /['"]\s*\\\n/.freeze
48
+ LINE_2_BEGINNING = /\A\s*['"]/.freeze
49
+ LEADING_STYLE_OFFENSE = /(?<trailing_spaces>\s+)(?<ending>#{LINE_1_ENDING})/.freeze
50
+ TRAILING_STYLE_OFFENSE = /(?<beginning>#{LINE_2_BEGINNING})(?<leading_spaces>\s+)/.freeze
51
+ private_constant :LINE_1_ENDING, :LINE_2_BEGINNING,
52
+ :LEADING_STYLE_OFFENSE, :TRAILING_STYLE_OFFENSE
45
53
 
46
54
  def on_dstr(node)
47
55
  end_of_first_line = node.loc.expression.begin_pos - node.loc.expression.column
@@ -52,9 +60,9 @@ module RuboCop
52
60
  next unless continuation?(raw_line_one)
53
61
 
54
62
  if enforced_style_leading?
55
- investigate_leading_style(raw_line_one, end_of_first_line)
63
+ investigate_leading_style(raw_line_one, raw_line_two, end_of_first_line)
56
64
  else
57
- investigate_trailing_style(raw_line_two, end_of_first_line)
65
+ investigate_trailing_style(raw_line_one, raw_line_two, end_of_first_line)
58
66
  end
59
67
  end
60
68
  end
@@ -65,24 +73,37 @@ module RuboCop
65
73
  processed_source.raw_source.lines[node.first_line - 1, line_range(node).size]
66
74
  end
67
75
 
68
- def investigate_leading_style(first_line, end_of_first_line)
69
- matches = first_line.match(/(?<trailing_spaces>\s+)(?<ending>['"]\s*\\\n)/)
76
+ def investigate_leading_style(first_line, second_line, end_of_first_line)
77
+ matches = first_line.match(LEADING_STYLE_OFFENSE)
70
78
  return if matches.nil?
71
79
 
72
- add_offense(leading_offense_range(end_of_first_line, matches))
80
+ offense_range = leading_offense_range(end_of_first_line, matches)
81
+ add_offense(offense_range) do |corrector|
82
+ insert_pos = end_of_first_line + second_line[LINE_2_BEGINNING].length
83
+ autocorrect(corrector, offense_range, insert_pos, matches[:trailing_spaces])
84
+ end
73
85
  end
74
86
 
75
- def investigate_trailing_style(second_line, end_of_first_line)
76
- matches = second_line.match(/\A(?<beginning>\s*['"])(?<leading_spaces>\s+)/)
87
+ def investigate_trailing_style(first_line, second_line, end_of_first_line)
88
+ matches = second_line.match(TRAILING_STYLE_OFFENSE)
77
89
  return if matches.nil?
78
90
 
79
- add_offense(trailing_offense_range(end_of_first_line, matches))
91
+ offense_range = trailing_offense_range(end_of_first_line, matches)
92
+ add_offense(offense_range) do |corrector|
93
+ insert_pos = end_of_first_line - first_line[LINE_1_ENDING].length
94
+ autocorrect(corrector, offense_range, insert_pos, matches[:leading_spaces])
95
+ end
80
96
  end
81
97
 
82
98
  def continuation?(line)
83
99
  line.end_with?("\\\n")
84
100
  end
85
101
 
102
+ def autocorrect(corrector, offense_range, insert_pos, spaces)
103
+ corrector.remove(offense_range)
104
+ corrector.replace(range_between(insert_pos, insert_pos), spaces)
105
+ end
106
+
86
107
  def leading_offense_range(end_of_first_line, matches)
87
108
  end_pos = end_of_first_line - matches[:ending].length
88
109
  begin_pos = end_pos - matches[:trailing_spaces].length
@@ -92,7 +92,7 @@ module RuboCop
92
92
  # which lines start inside a string literal?
93
93
  return [] if ast.nil?
94
94
 
95
- ast.each_node(:str, :dstr).each_with_object(Set.new) do |str, ranges|
95
+ ast.each_node(:str, :dstr).with_object(Set.new) do |str, ranges|
96
96
  loc = str.location
97
97
 
98
98
  if str.heredoc?
@@ -169,19 +169,22 @@ module RuboCop
169
169
 
170
170
  def qualifies_for_compact?(node, token, side: :right)
171
171
  if side == :right
172
- multi_dimensional_array?(node, token) && !next_to_bracket?(token)
172
+ multi_dimensional_array?(node, token) && token.space_before?
173
173
  else
174
- multi_dimensional_array?(node, token, side: :left) &&
175
- !next_to_bracket?(token, side: :left)
174
+ multi_dimensional_array?(node, token, side: :left) && token.space_after?
176
175
  end
177
176
  end
178
177
 
179
178
  def multi_dimensional_array?(node, token, side: :right)
180
- i = index_for(node, token)
179
+ offset = side == :right ? -1 : +1
180
+ i = index_for(node, token) + offset
181
+ # TODO: change this type check once
182
+ # https://github.com/rubocop/rubocop-ast/pull/240 is merged
183
+ i += offset while processed_source.tokens_within(node)[i].new_line?
181
184
  if side == :right
182
- processed_source.tokens_within(node)[i - 1].right_bracket?
185
+ processed_source.tokens_within(node)[i].right_bracket?
183
186
  else
184
- processed_source.tokens_within(node)[i + 1].left_array_bracket?
187
+ processed_source.tokens_within(node)[i].left_array_bracket?
185
188
  end
186
189
  end
187
190
 
@@ -200,12 +203,13 @@ module RuboCop
200
203
  end
201
204
 
202
205
  def compact_corrections(corrector, node, left, right)
203
- if qualifies_for_compact?(node, left, side: :left)
206
+ if multi_dimensional_array?(node, left, side: :left)
204
207
  compact(corrector, left, :right)
205
208
  elsif !left.space_after?
206
209
  corrector.insert_after(left.pos, ' ')
207
210
  end
208
- if qualifies_for_compact?(node, right)
211
+
212
+ if multi_dimensional_array?(node, right)
209
213
  compact(corrector, right, :left)
210
214
  elsif !right.space_before?
211
215
  corrector.insert_before(right.pos, ' ')
@@ -213,7 +217,7 @@ module RuboCop
213
217
  end
214
218
 
215
219
  def compact(corrector, bracket, side)
216
- range = side_space_range(range: bracket.pos, side: side)
220
+ range = side_space_range(range: bracket.pos, side: side, include_newlines: true)
217
221
  corrector.remove(range)
218
222
  end
219
223
  end
@@ -6,6 +6,9 @@ module RuboCop
6
6
  # Checks for unnecessary additional spaces inside array percent literals
7
7
  # (i.e. %i/%w).
8
8
  #
9
+ # Note that blank percent literals (e.g. `%i( )`) are checked by
10
+ # `Layout/SpaceInsidePercentLiteralDelimiters`.
11
+ #
9
12
  # @example
10
13
  #
11
14
  # # bad
@@ -46,10 +46,13 @@ module RuboCop
46
46
  # # bad
47
47
  # foo = { }
48
48
  # bar = { }
49
+ # baz = {
50
+ # }
49
51
  #
50
52
  # # good
51
53
  # foo = {}
52
54
  # bar = {}
55
+ # baz = {}
53
56
  #
54
57
  # @example EnforcedStyleForEmptyBraces: space
55
58
  # # The `space` EnforcedStyleForEmptyBraces style enforces that
@@ -60,8 +63,9 @@ module RuboCop
60
63
  #
61
64
  # # good
62
65
  # foo = { }
63
- # foo = { }
64
- # foo = { }
66
+ # foo = { }
67
+ # foo = {
68
+ # }
65
69
  #
66
70
  class SpaceInsideHashLiteralBraces < Base
67
71
  include SurroundingSpace
@@ -77,6 +81,7 @@ module RuboCop
77
81
 
78
82
  check(tokens[0], tokens[1])
79
83
  check(tokens[-2], tokens[-1]) if tokens.size > 2
84
+ check_whitespace_only_hash(node) if enforce_no_space_style_for_empty_braces?
80
85
  end
81
86
 
82
87
  private
@@ -103,7 +108,7 @@ module RuboCop
103
108
  if is_same_braces && style == :compact
104
109
  false
105
110
  elsif is_empty_braces
106
- cop_config['EnforcedStyleForEmptyBraces'] != 'no_space'
111
+ !enforce_no_space_style_for_empty_braces?
107
112
  else
108
113
  style != :no_space
109
114
  end
@@ -175,6 +180,28 @@ module RuboCop
175
180
 
176
181
  range_between(begin_pos, range.end_pos - 1)
177
182
  end
183
+
184
+ def check_whitespace_only_hash(node)
185
+ range = range_inside_hash(node)
186
+ return unless range.source.match?(/\A\s+\z/m)
187
+
188
+ add_offense(
189
+ range,
190
+ message: format(MSG, problem: 'empty hash literal braces detected')
191
+ ) do |corrector|
192
+ corrector.remove(range)
193
+ end
194
+ end
195
+
196
+ def range_inside_hash(node)
197
+ return node.source_range if node.location.begin.nil?
198
+
199
+ range_between(node.location.begin.end_pos, node.location.end.begin_pos)
200
+ end
201
+
202
+ def enforce_no_space_style_for_empty_braces?
203
+ cop_config['EnforcedStyleForEmptyBraces'] == 'no_space'
204
+ end
178
205
  end
179
206
  end
180
207
  end
@@ -8,14 +8,31 @@ module RuboCop
8
8
  #
9
9
  # @example
10
10
  #
11
+ # # bad
12
+ # %i( foo bar baz )
13
+ #
11
14
  # # good
12
15
  # %i(foo bar baz)
13
16
  #
14
17
  # # bad
15
18
  # %w( foo bar baz )
16
19
  #
20
+ # # good
21
+ # %w(foo bar baz)
22
+ #
17
23
  # # bad
18
24
  # %x( ls -l )
25
+ #
26
+ # # good
27
+ # %x(ls -l)
28
+ #
29
+ # # bad
30
+ # %w( )
31
+ # %w(
32
+ # )
33
+ #
34
+ # # good
35
+ # %w()
19
36
  class SpaceInsidePercentLiteralDelimiters < Base
20
37
  include MatchRange
21
38
  include PercentLiteral
@@ -34,11 +51,21 @@ module RuboCop
34
51
  end
35
52
 
36
53
  def on_percent_literal(node)
54
+ add_offenses_for_blank_spaces(node)
37
55
  add_offenses_for_unnecessary_spaces(node)
38
56
  end
39
57
 
40
58
  private
41
59
 
60
+ def add_offenses_for_blank_spaces(node)
61
+ range = body_range(node)
62
+ return if range.source.empty? || !range.source.strip.empty?
63
+
64
+ add_offense(range) do |corrector|
65
+ corrector.remove(range)
66
+ end
67
+ end
68
+
42
69
  def add_offenses_for_unnecessary_spaces(node)
43
70
  return unless node.single_line?
44
71
 
@@ -54,6 +81,13 @@ module RuboCop
54
81
  each_match_range(contents_range(node), regex, &blk)
55
82
  end
56
83
  end
84
+
85
+ def body_range(node)
86
+ node.location.expression.with(
87
+ begin_pos: node.location.begin.end_pos,
88
+ end_pos: node.location.end.begin_pos
89
+ )
90
+ end
57
91
  end
58
92
  end
59
93
  end
@@ -38,6 +38,8 @@ module RuboCop
38
38
  # # bad
39
39
  # foo[ ]
40
40
  # foo[ ]
41
+ # foo[
42
+ # ]
41
43
  #
42
44
  # # good
43
45
  # foo[]
@@ -49,6 +51,8 @@ module RuboCop
49
51
  # # bad
50
52
  # foo[]
51
53
  # foo[ ]
54
+ # foo[
55
+ # ]
52
56
  #
53
57
  # # good
54
58
  # foo[ ]
@@ -64,8 +68,6 @@ module RuboCop
64
68
  RESTRICT_ON_SEND = %i[[] []=].freeze
65
69
 
66
70
  def on_send(node)
67
- return if node.multiline?
68
-
69
71
  tokens = processed_source.tokens_within(node)
70
72
  left_token = left_ref_bracket(node, tokens)
71
73
  return unless left_token
@@ -76,6 +78,8 @@ module RuboCop
76
78
  return empty_offenses(node, left_token, right_token, EMPTY_MSG)
77
79
  end
78
80
 
81
+ return if node.multiline?
82
+
79
83
  if style == :no_space
80
84
  no_space_offenses(node, left_token, right_token, MSG)
81
85
  else
@@ -45,7 +45,7 @@ module RuboCop
45
45
  # # bad
46
46
  # expect { do_something }.to change { object.attribute }
47
47
  #
48
- # @example AllowedPatterns: [/change/]
48
+ # @example AllowedPatterns: ['change']
49
49
  #
50
50
  # # good
51
51
  # expect { do_something }.to change { object.attribute }
@@ -63,7 +63,7 @@ module RuboCop
63
63
  return unless node.arguments?
64
64
 
65
65
  return unless ambiguous_block_association?(node)
66
- return if node.parenthesized? || node.last_argument.lambda? || node.last_argument.proc? ||
66
+ return if node.parenthesized? || node.last_argument.lambda_or_proc? ||
67
67
  allowed_method_pattern?(node)
68
68
 
69
69
  message = message(node)
@@ -11,6 +11,10 @@ module RuboCop
11
11
  # an assignment to indicate "I know I'm using an assignment
12
12
  # as a condition. It's not a mistake."
13
13
  #
14
+ # @safety
15
+ # This cop's autocorrection is unsafe because it assumes that
16
+ # the author meant to use an assignment result as a condition.
17
+ #
14
18
  # @example
15
19
  # # bad
16
20
  # if some_var = true
@@ -35,6 +39,8 @@ module RuboCop
35
39
  # end
36
40
  #
37
41
  class AssignmentInCondition < Base
42
+ extend AutoCorrector
43
+
38
44
  include SafeAssignment
39
45
 
40
46
  MSG_WITH_SAFE_ASSIGNMENT_ALLOWED =
@@ -53,7 +59,11 @@ module RuboCop
53
59
  next :skip_children if skip_children?(asgn_node)
54
60
  next if allowed_construct?(asgn_node)
55
61
 
56
- add_offense(asgn_node.loc.operator)
62
+ add_offense(asgn_node.loc.operator) do |corrector|
63
+ next unless safe_assignment_allowed?
64
+
65
+ corrector.wrap(asgn_node, '(', ')')
66
+ end
57
67
  end
58
68
  end
59
69
  alias on_while on_if
@@ -14,7 +14,8 @@ module RuboCop
14
14
  # Alternative: 'alternative_value'
15
15
  # DeprecatedVersion: 'deprecated_version'
16
16
  #
17
- # By default, `NIL`, `TRUE`, `FALSE` and `Random::DEFAULT` are configured.
17
+ # By default, `NIL`, `TRUE`, `FALSE`, `Net::HTTPServerException, `Random::DEFAULT`,
18
+ # `Struct::Group`, and `Struct::Passwd` are configured.
18
19
  #
19
20
  # @example
20
21
  #
@@ -22,13 +23,19 @@ module RuboCop
22
23
  # NIL
23
24
  # TRUE
24
25
  # FALSE
26
+ # Net::HTTPServerException
25
27
  # Random::DEFAULT # Return value of Ruby 2 is `Random` instance, Ruby 3.0 is `Random` class.
28
+ # Struct::Group
29
+ # Struct::Passwd
26
30
  #
27
31
  # # good
28
32
  # nil
29
33
  # true
30
34
  # false
35
+ # Net::HTTPClientException
31
36
  # Random.new # `::DEFAULT` has been deprecated in Ruby 3, `.new` is compatible with Ruby 2.
37
+ # Etc::Group
38
+ # Etc::Passwd
32
39
  #
33
40
  class DeprecatedConstants < Base
34
41
  extend AutoCorrector
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for duplicated magic comments.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ #
12
+ # # encoding: ascii
13
+ # # encoding: ascii
14
+ #
15
+ # # good
16
+ #
17
+ # # encoding: ascii
18
+ #
19
+ # # bad
20
+ #
21
+ # # frozen_string_literal: true
22
+ # # frozen_string_literal: true
23
+ #
24
+ # # good
25
+ #
26
+ # # frozen_string_literal: true
27
+ #
28
+ class DuplicateMagicComment < Base
29
+ include FrozenStringLiteral
30
+ include RangeHelp
31
+ extend AutoCorrector
32
+
33
+ MSG = 'Duplicate magic comment detected.'
34
+
35
+ def on_new_investigation
36
+ return if processed_source.buffer.source.empty?
37
+
38
+ magic_comment_lines.each_value do |comment_lines|
39
+ next if comment_lines.count <= 1
40
+
41
+ comment_lines[1..].each do |comment_line|
42
+ range = processed_source.buffer.line_range(comment_line + 1)
43
+
44
+ register_offense(range)
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def magic_comment_lines
52
+ comment_lines = { encoding_magic_comments: [], frozen_string_literal_magic_comments: [] }
53
+
54
+ leading_magic_comments.each.with_index do |magic_comment, index|
55
+ if magic_comment.encoding_specified?
56
+ comment_lines[:encoding_magic_comments] << index
57
+ elsif magic_comment.frozen_string_literal_specified?
58
+ comment_lines[:frozen_string_literal_magic_comments] << index
59
+ end
60
+ end
61
+
62
+ comment_lines
63
+ end
64
+
65
+ def register_offense(range)
66
+ add_offense(range) do |corrector|
67
+ corrector.remove(range_by_whole_lines(range, include_final_newline: true))
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end