rubocop 1.51.0 → 1.54.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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +62 -3
  4. data/lib/rubocop/cli/command/lsp.rb +19 -0
  5. data/lib/rubocop/cli.rb +3 -0
  6. data/lib/rubocop/config_loader_resolver.rb +4 -3
  7. data/lib/rubocop/cop/base.rb +1 -1
  8. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  9. data/lib/rubocop/cop/bundler/gem_version.rb +2 -2
  10. data/lib/rubocop/cop/gemspec/dependency_version.rb +2 -2
  11. data/lib/rubocop/cop/internal_affairs/cop_description.rb +32 -8
  12. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
  13. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +3 -3
  14. data/lib/rubocop/cop/layout/class_structure.rb +7 -0
  15. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +1 -2
  16. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +27 -4
  17. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +2 -0
  18. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  19. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  20. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
  21. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  22. data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
  23. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -1
  24. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +1 -1
  25. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -1
  26. data/lib/rubocop/cop/lint/debugger.rb +9 -5
  27. data/lib/rubocop/cop/lint/duplicate_hash_key.rb +2 -1
  28. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +46 -19
  29. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -2
  30. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +1 -1
  31. data/lib/rubocop/cop/lint/identity_comparison.rb +0 -1
  32. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +1 -2
  33. data/lib/rubocop/cop/lint/inherit_exception.rb +9 -0
  34. data/lib/rubocop/cop/lint/missing_super.rb +34 -5
  35. data/lib/rubocop/cop/lint/mixed_case_range.rb +111 -0
  36. data/lib/rubocop/cop/lint/number_conversion.rb +5 -0
  37. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +0 -1
  38. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +130 -0
  39. data/lib/rubocop/cop/lint/redundant_require_statement.rb +8 -3
  40. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -2
  41. data/lib/rubocop/cop/lint/shadowed_exception.rb +5 -11
  42. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  43. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  44. data/lib/rubocop/cop/lint/useless_assignment.rb +4 -1
  45. data/lib/rubocop/cop/lint/void.rb +12 -18
  46. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
  47. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +30 -2
  48. data/lib/rubocop/cop/migration/department_name.rb +2 -2
  49. data/lib/rubocop/cop/mixin/allowed_receivers.rb +34 -0
  50. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  51. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -1
  52. data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
  53. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  54. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  55. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +3 -3
  56. data/lib/rubocop/cop/naming/variable_name.rb +6 -1
  57. data/lib/rubocop/cop/style/accessor_grouping.rb +5 -1
  58. data/lib/rubocop/cop/style/begin_block.rb +1 -2
  59. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  60. data/lib/rubocop/cop/style/block_delimiters.rb +3 -3
  61. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  62. data/lib/rubocop/cop/style/class_equality_comparison.rb +17 -39
  63. data/lib/rubocop/cop/style/collection_compact.rb +6 -0
  64. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  65. data/lib/rubocop/cop/style/dir.rb +1 -1
  66. data/lib/rubocop/cop/style/dir_empty.rb +8 -14
  67. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  68. data/lib/rubocop/cop/style/eval_with_location.rb +4 -4
  69. data/lib/rubocop/cop/style/exact_regexp_match.rb +8 -2
  70. data/lib/rubocop/cop/style/file_read.rb +2 -2
  71. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -22
  72. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  73. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  74. data/lib/rubocop/cop/style/identical_conditional_branches.rb +6 -2
  75. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  76. data/lib/rubocop/cop/style/invertible_unless_condition.rb +1 -1
  77. data/lib/rubocop/cop/style/lambda.rb +3 -3
  78. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +3 -4
  79. data/lib/rubocop/cop/style/multiple_comparison.rb +14 -0
  80. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  81. data/lib/rubocop/cop/style/preferred_hash_methods.rb +1 -1
  82. data/lib/rubocop/cop/style/redundant_array_constructor.rb +77 -0
  83. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  84. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -1
  85. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +38 -0
  86. data/lib/rubocop/cop/style/redundant_filter_chain.rb +101 -0
  87. data/lib/rubocop/cop/style/redundant_line_continuation.rb +2 -2
  88. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  89. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +100 -0
  90. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +46 -0
  91. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  92. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +3 -1
  93. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  94. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -0
  95. data/lib/rubocop/cop/style/require_order.rb +2 -1
  96. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -3
  97. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +81 -0
  98. data/lib/rubocop/cop/style/select_by_regexp.rb +15 -5
  99. data/lib/rubocop/cop/style/signal_exception.rb +1 -1
  100. data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
  101. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  102. data/lib/rubocop/cop/style/special_global_vars.rb +1 -2
  103. data/lib/rubocop/cop/style/yaml_file_read.rb +66 -0
  104. data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
  105. data/lib/rubocop/cop/util.rb +1 -1
  106. data/lib/rubocop/cop/utils/regexp_ranges.rb +100 -0
  107. data/lib/rubocop/cop/variable_force/assignment.rb +43 -4
  108. data/lib/rubocop/cop/variable_force.rb +1 -0
  109. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  110. data/lib/rubocop/ext/regexp_parser.rb +4 -1
  111. data/lib/rubocop/lsp/logger.rb +22 -0
  112. data/lib/rubocop/lsp/routes.rb +231 -0
  113. data/lib/rubocop/lsp/runtime.rb +82 -0
  114. data/lib/rubocop/lsp/server.rb +66 -0
  115. data/lib/rubocop/lsp/severity.rb +27 -0
  116. data/lib/rubocop/options.rb +11 -1
  117. data/lib/rubocop/server/client_command/exec.rb +2 -1
  118. data/lib/rubocop/version.rb +8 -4
  119. data/lib/rubocop.rb +12 -0
  120. metadata +36 -5
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `_.each_with_object({}) {...}`,
7
- # `_.map {...}.to_h`, and `Hash[_.map {...}]` that are actually just
6
+ # Looks for uses of `\_.each_with_object({}) {...}`,
7
+ # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
8
8
  # transforming the values of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_values` instead.
10
10
  #
@@ -158,13 +158,16 @@ module RuboCop
158
158
  return false unless expressions.size >= 1 && unique_expressions.one?
159
159
 
160
160
  unique_expression = unique_expressions.first
161
- return true unless unique_expression.assignment?
161
+ return true unless unique_expression&.assignment?
162
162
 
163
163
  lhs = unique_expression.child_nodes.first
164
164
  node.condition.child_nodes.none? { |n| n.source == lhs.source if n.variable? }
165
165
  end
166
166
 
167
- def check_expressions(node, expressions, insert_position) # rubocop:disable Metrics/MethodLength
167
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
168
+ def check_expressions(node, expressions, insert_position)
169
+ return if expressions.any?(&:nil?)
170
+
168
171
  inserted_expression = false
169
172
 
170
173
  expressions.each do |expression|
@@ -184,6 +187,7 @@ module RuboCop
184
187
  end
185
188
  end
186
189
  end
190
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
187
191
 
188
192
  def last_child_of_parent?(node)
189
193
  return true unless (parent = node.parent)
@@ -47,7 +47,7 @@ module RuboCop
47
47
  def correct_elsif(node)
48
48
  <<~RUBY.chop
49
49
  if #{node.condition.source}
50
- #{node.if_branch.source}
50
+ #{node.if_branch&.source}
51
51
  #{build_else_branch(node.else_branch).chop}
52
52
  end
53
53
  RUBY
@@ -56,7 +56,7 @@ module RuboCop
56
56
  def build_else_branch(second_condition)
57
57
  result = <<~RUBY
58
58
  elsif #{second_condition.condition.source}
59
- #{second_condition.if_branch.source}
59
+ #{second_condition.if_branch&.source}
60
60
  RUBY
61
61
 
62
62
  if second_condition.else_branch
@@ -72,7 +72,7 @@ module RuboCop
72
72
  when :begin
73
73
  invertible?(node.children.first)
74
74
  when :send
75
- return if inheritance_check?(node)
75
+ return false if inheritance_check?(node)
76
76
 
77
77
  node.method?(:!) || inverse_methods.key?(node.method_name)
78
78
  when :or, :and
@@ -69,10 +69,10 @@ module RuboCop
69
69
  return unless offending_selector?(node, selector)
70
70
 
71
71
  add_offense(node.send_node.source_range, message: message(node, selector)) do |corrector|
72
- if node.send_node.source == 'lambda'
73
- autocorrect_method_to_literal(corrector, node)
74
- else
72
+ if node.send_node.lambda_literal?
75
73
  LambdaLiteralToMethodCorrector.new(node).call(corrector)
74
+ else
75
+ autocorrect_method_to_literal(corrector, node)
76
76
  end
77
77
  end
78
78
  end
@@ -98,7 +98,7 @@ module RuboCop
98
98
 
99
99
  def call_in_literals?(node)
100
100
  parent = node.parent&.block_type? ? node.parent.parent : node.parent
101
- return unless parent
101
+ return false unless parent
102
102
 
103
103
  parent.pair_type? ||
104
104
  parent.array_type? ||
@@ -109,7 +109,7 @@ module RuboCop
109
109
 
110
110
  def call_in_logical_operators?(node)
111
111
  parent = node.parent&.block_type? ? node.parent.parent : node.parent
112
- return unless parent
112
+ return false unless parent
113
113
 
114
114
  logical_operator?(parent) ||
115
115
  (parent.send_type? &&
@@ -135,8 +135,7 @@ module RuboCop
135
135
  end
136
136
 
137
137
  def call_with_braced_block?(node)
138
- (node.send_type? || node.super_type?) &&
139
- ((node.parent&.block_type? || node.parent&.numblock_type?) && node.parent&.braces?)
138
+ (node.send_type? || node.super_type?) && node.block_node&.braces?
140
139
  end
141
140
 
142
141
  def call_as_argument_or_chain?(node)
@@ -40,6 +40,15 @@ module RuboCop
40
40
  #
41
41
  # # good
42
42
  # foo if [b.lightweight, b.heavyweight].include?(a)
43
+ #
44
+ # @example ComparisonsThreshold: 2 (default)
45
+ # # bad
46
+ # foo if a == 'a' || a == 'b'
47
+ #
48
+ # @example ComparisonsThreshold: 3
49
+ # # good
50
+ # foo if a == 'a' || a == 'b'
51
+ #
43
52
  class MultipleComparison < Base
44
53
  extend AutoCorrector
45
54
 
@@ -58,6 +67,7 @@ module RuboCop
58
67
  return unless node == root_of_or_node
59
68
  return unless nested_variable_comparison?(root_of_or_node)
60
69
  return if @allowed_method_comparison
70
+ return if @compared_elements.size < comparisons_threshold
61
71
 
62
72
  add_offense(node) do |corrector|
63
73
  elements = @compared_elements.join(', ')
@@ -151,6 +161,10 @@ module RuboCop
151
161
  def allow_method_comparison?
152
162
  cop_config.fetch('AllowMethodComparison', true)
153
163
  end
164
+
165
+ def comparisons_threshold
166
+ cop_config.fetch('ComparisonsThreshold', 2)
167
+ end
154
168
  end
155
169
  end
156
170
  end
@@ -121,7 +121,7 @@ module RuboCop
121
121
 
122
122
  def allowed_patterns
123
123
  # Convert the patterns to be anchored
124
- super.map { |regexp| Regexp.new(/\A#{regexp}\z/) }
124
+ super.map { |regexp| /\A#{regexp}\z/ }
125
125
  end
126
126
  end
127
127
  end
@@ -61,7 +61,7 @@ module RuboCop
61
61
  if style == :verbose
62
62
  "has_#{method_name}"
63
63
  else
64
- method_name.to_s.sub(/has_/, '')
64
+ method_name.to_s.delete_prefix('has_')
65
65
  end
66
66
  end
67
67
 
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for the instantiation of array using redundant `Array` constructor.
7
+ # Autocorrect replaces to array literal which is the simplest and fastest.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # Array.new([])
13
+ # Array[]
14
+ # Array([])
15
+ # Array.new(['foo', 'foo', 'foo'])
16
+ # Array['foo', 'foo', 'foo']
17
+ # Array(['foo', 'foo', 'foo'])
18
+ #
19
+ # # good
20
+ # []
21
+ # ['foo', 'foo', 'foo']
22
+ # Array.new(3, 'foo')
23
+ # Array.new(3) { 'foo' }
24
+ #
25
+ class RedundantArrayConstructor < Base
26
+ extend AutoCorrector
27
+
28
+ MSG = 'Remove the redundant `Array` constructor.'
29
+
30
+ RESTRICT_ON_SEND = %i[new [] Array].freeze
31
+
32
+ # @!method redundant_array_constructor(node)
33
+ def_node_matcher :redundant_array_constructor, <<~PATTERN
34
+ {
35
+ (send
36
+ (const {nil? cbase} :Array) :new
37
+ $(array ...))
38
+ (send
39
+ (const {nil? cbase} :Array) :[]
40
+ $...)
41
+ (send
42
+ nil? :Array
43
+ $(array ...))
44
+ }
45
+ PATTERN
46
+
47
+ def on_send(node)
48
+ return unless (array_literal = redundant_array_constructor(node))
49
+
50
+ receiver = node.receiver
51
+ selector = node.loc.selector
52
+
53
+ if node.method?(:new)
54
+ range = receiver.source_range.join(selector)
55
+ replacement = array_literal
56
+ elsif node.method?(:Array)
57
+ range = selector
58
+ replacement = array_literal
59
+ else
60
+ range = receiver
61
+ replacement = selector.begin.join(node.source_range.end)
62
+ end
63
+
64
+ register_offense(range, node, replacement)
65
+ end
66
+
67
+ private
68
+
69
+ def register_offense(range, node, replacement)
70
+ add_offense(range) do |corrector|
71
+ corrector.replace(node, replacement.source)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -146,7 +146,7 @@ module RuboCop
146
146
  end
147
147
 
148
148
  def use_modifier_form_after_multiline_begin_block?(node)
149
- return unless (parent = node.parent)
149
+ return false unless (parent = node.parent)
150
150
 
151
151
  node.multiline? && parent.if_type? && parent.modifier_form?
152
152
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
  RUBY
64
64
 
65
65
  def offense?(node)
66
- return if node.modifier_form?
66
+ return false if node.modifier_form?
67
67
 
68
68
  redundant_condition?(node) || redundant_condition_inverted?(node)
69
69
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for uses a redundant current directory in path.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # require_relative './path/to/feature'
12
+ #
13
+ # # good
14
+ # require_relative 'path/to/feature'
15
+ #
16
+ class RedundantCurrentDirectoryInPath < Base
17
+ include RangeHelp
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Remove the redundant current directory path.'
21
+ CURRENT_DIRECTORY_PATH = './'
22
+
23
+ def on_send(node)
24
+ return unless node.method?(:require_relative)
25
+ return unless node.first_argument.str_content&.start_with?(CURRENT_DIRECTORY_PATH)
26
+ return unless (index = node.first_argument.source.index(CURRENT_DIRECTORY_PATH))
27
+
28
+ begin_pos = node.first_argument.source_range.begin.begin_pos + index
29
+ range = range_between(begin_pos, begin_pos + 2)
30
+
31
+ add_offense(range) do |corrector|
32
+ corrector.remove(range)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies usages of `any?`, `empty?` or `none?` predicate methods
7
+ # chained to `select`/`filter`/`find_all` and change them to use predicate method instead.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # arr.select { |x| x > 1 }.any?
12
+ #
13
+ # # good
14
+ # arr.any? { |x| x > 1 }
15
+ #
16
+ # # bad
17
+ # arr.select { |x| x > 1 }.empty?
18
+ # arr.select { |x| x > 1 }.none?
19
+ #
20
+ # # good
21
+ # arr.none? { |x| x > 1 }
22
+ #
23
+ # # good
24
+ # relation.select(:name).any?
25
+ # arr.select { |x| x > 1 }.any?(&:odd?)
26
+ #
27
+ # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
28
+ # # good
29
+ # arr.select { |x| x > 1 }.many?
30
+ #
31
+ # @example AllCops:ActiveSupportExtensionsEnabled: true
32
+ # # bad
33
+ # arr.select { |x| x > 1 }.many?
34
+ #
35
+ # # good
36
+ # arr.many? { |x| x > 1 }
37
+ #
38
+ class RedundantFilterChain < Base
39
+ extend AutoCorrector
40
+
41
+ MSG = 'Use `%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
42
+
43
+ RAILS_METHODS = %i[many?].freeze
44
+ RESTRICT_ON_SEND = (%i[any? empty? none? one?] + RAILS_METHODS).freeze
45
+
46
+ # @!method select_predicate?(node)
47
+ def_node_matcher :select_predicate?, <<~PATTERN
48
+ (send
49
+ {
50
+ (block $(send _ {:select :filter :find_all}) ...)
51
+ $(send _ {:select :filter :find_all} block_pass_type?)
52
+ }
53
+ ${:#{RESTRICT_ON_SEND.join(' :')}})
54
+ PATTERN
55
+
56
+ REPLACEMENT_METHODS = {
57
+ any?: :any?,
58
+ empty?: :none?,
59
+ none?: :none?,
60
+ one?: :one?,
61
+ many?: :many?
62
+ }.freeze
63
+ private_constant :REPLACEMENT_METHODS
64
+
65
+ def on_send(node)
66
+ return if node.arguments? || node.block_node
67
+
68
+ select_predicate?(node) do |select_node, filter_method|
69
+ return if RAILS_METHODS.include?(filter_method) && !active_support_extensions_enabled?
70
+
71
+ register_offense(select_node, node)
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def register_offense(select_node, predicate_node)
78
+ replacement = REPLACEMENT_METHODS[predicate_node.method_name]
79
+ message = format(MSG, prefer: replacement,
80
+ first_method: select_node.method_name,
81
+ second_method: predicate_node.method_name)
82
+
83
+ offense_range = offense_range(select_node, predicate_node)
84
+
85
+ add_offense(offense_range, message: message) do |corrector|
86
+ corrector.remove(predicate_range(predicate_node))
87
+ corrector.replace(select_node.loc.selector, replacement)
88
+ end
89
+ end
90
+
91
+ def offense_range(select_node, predicate_node)
92
+ select_node.loc.selector.join(predicate_node.loc.selector)
93
+ end
94
+
95
+ def predicate_range(predicate_node)
96
+ predicate_node.receiver.source_range.end.join(predicate_node.loc.selector)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -116,7 +116,7 @@ module RuboCop
116
116
  return false if argument_newline?(node)
117
117
 
118
118
  source = node.parent ? node.parent.source : node.source
119
- parse(source.gsub(/\\\n/, "\n")).valid_syntax?
119
+ parse(source.gsub("\\\n", "\n")).valid_syntax?
120
120
  end
121
121
 
122
122
  def inside_string_literal?(range, token)
@@ -150,7 +150,7 @@ module RuboCop
150
150
  end
151
151
 
152
152
  def same_line?(node, line)
153
- return unless (source_range = node.source_range)
153
+ return false unless (source_range = node.source_range)
154
154
 
155
155
  if node.is_a?(AST::StrNode)
156
156
  if node.heredoc?
@@ -85,7 +85,7 @@ module RuboCop
85
85
  end
86
86
 
87
87
  def allowed_ternary?(node)
88
- return unless node&.parent&.if_type?
88
+ return false unless node&.parent&.if_type?
89
89
 
90
90
  node.parent.ternary? && ternary_parentheses_required?
91
91
  end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies places where argument can be replaced from
7
+ # a deterministic regexp to a string.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # 'foo'.byteindex(/f/)
12
+ # 'foo'.byterindex(/f/)
13
+ # 'foo'.gsub(/f/, 'x')
14
+ # 'foo'.gsub!(/f/, 'x')
15
+ # 'foo'.partition(/f/)
16
+ # 'foo'.rpartition(/f/)
17
+ # 'foo'.scan(/f/)
18
+ # 'foo'.split(/f/)
19
+ # 'foo'.start_with?(/f/)
20
+ # 'foo'.sub(/f/, 'x')
21
+ # 'foo'.sub!(/f/, 'x')
22
+ #
23
+ # # good
24
+ # 'foo'.byteindex('f')
25
+ # 'foo'.byterindex('f')
26
+ # 'foo'.gsub('f', 'x')
27
+ # 'foo'.gsub!('f', 'x')
28
+ # 'foo'.partition('f')
29
+ # 'foo'.rpartition('f')
30
+ # 'foo'.scan('f')
31
+ # 'foo'.split('f')
32
+ # 'foo'.start_with?('f')
33
+ # 'foo'.sub('f', 'x')
34
+ # 'foo'.sub!('f', 'x')
35
+ class RedundantRegexpArgument < Base
36
+ extend AutoCorrector
37
+
38
+ MSG = 'Use string `%<prefer>s` as argument instead of regexp `%<current>s`.'
39
+ RESTRICT_ON_SEND = %i[
40
+ byteindex byterindex gsub gsub! partition rpartition scan split start_with? sub sub!
41
+ ].freeze
42
+ DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
43
+ STR_SPECIAL_CHARS = %w[
44
+ \a \c \C \e \f \M \n \" \' \\\\ \t \b \f \r \u \v \x \0 \1 \2 \3 \4 \5 \6 \7
45
+ ].freeze
46
+
47
+ def on_send(node)
48
+ return unless (regexp_node = node.first_argument)
49
+ return unless regexp_node.regexp_type?
50
+ return if !regexp_node.regopt.children.empty? || regexp_node.content == ' '
51
+ return unless determinist_regexp?(regexp_node)
52
+
53
+ prefer = preferred_argument(regexp_node)
54
+ message = format(MSG, prefer: prefer, current: regexp_node.source)
55
+
56
+ add_offense(regexp_node, message: message) do |corrector|
57
+ corrector.replace(regexp_node, prefer)
58
+ end
59
+ end
60
+ alias on_csend on_send
61
+
62
+ private
63
+
64
+ def determinist_regexp?(regexp_node)
65
+ DETERMINISTIC_REGEX.match?(regexp_node.source)
66
+ end
67
+
68
+ def preferred_argument(regexp_node)
69
+ new_argument = replacement(regexp_node)
70
+
71
+ if new_argument.include?('"')
72
+ new_argument.gsub!("'", "\\\\'")
73
+ quote = "'"
74
+ else
75
+ quote = '"'
76
+ end
77
+
78
+ "#{quote}#{new_argument}#{quote}"
79
+ end
80
+
81
+ def replacement(regexp_node)
82
+ regexp_content = regexp_node.content
83
+ stack = []
84
+ chars = regexp_content.chars.each_with_object([]) do |char, strings|
85
+ if stack.empty? && char == '\\'
86
+ stack.push(char)
87
+ else
88
+ strings << "#{stack.pop}#{char}"
89
+ end
90
+ end
91
+ chars.map do |char|
92
+ char = char.dup
93
+ char.delete!('\\') unless STR_SPECIAL_CHARS.include?(char)
94
+ char
95
+ end.join
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for the instantiation of regexp using redundant `Regexp.new` or `Regexp.compile`.
7
+ # Autocorrect replaces to regexp literal which is the simplest and fastest.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # Regexp.new(/regexp/)
13
+ # Regexp.compile(/regexp/)
14
+ #
15
+ # # good
16
+ # /regexp/
17
+ # Regexp.new('regexp')
18
+ # Regexp.compile('regexp')
19
+ #
20
+ class RedundantRegexpConstructor < Base
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Remove the redundant `Regexp.%<method>s`.'
24
+ RESTRICT_ON_SEND = %i[new compile].freeze
25
+
26
+ # @!method redundant_regexp_constructor(node)
27
+ def_node_matcher :redundant_regexp_constructor, <<~PATTERN
28
+ (send
29
+ (const {nil? cbase} :Regexp) {:new :compile}
30
+ (regexp $... (regopt $...)))
31
+ PATTERN
32
+
33
+ def on_send(node)
34
+ return unless (regexp, regopt = redundant_regexp_constructor(node))
35
+
36
+ add_offense(node, message: format(MSG, method: node.method_name)) do |corrector|
37
+ pattern = regexp.map(&:source).join
38
+ regopt = regopt.join
39
+
40
+ corrector.replace(node, "/#{pattern}/#{regopt}")
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -44,7 +44,8 @@ module RuboCop
44
44
 
45
45
  def on_regexp(node)
46
46
  each_escape(node) do |char, index, within_character_class|
47
- next if allowed_escape?(node, char, index, within_character_class)
47
+ next if char.valid_encoding? && allowed_escape?(node, char, index,
48
+ within_character_class)
48
49
 
49
50
  location = escape_range_at_index(node, index)
50
51
 
@@ -62,7 +62,9 @@ module RuboCop
62
62
  end
63
63
 
64
64
  def multiple_statements?(branch)
65
- branch && branch.children.compact.count > 1
65
+ return false unless branch&.begin_type?
66
+
67
+ !branch.children.empty?
66
68
  end
67
69
 
68
70
  def self_assign?(variable, branch)
@@ -198,7 +198,7 @@ module RuboCop
198
198
  end
199
199
 
200
200
  def with_logical_operator?(node)
201
- return unless (parent = node.parent)
201
+ return false unless (parent = node.parent)
202
202
 
203
203
  parent.or_type? || parent.and_type?
204
204
  end
@@ -157,6 +157,8 @@ module RuboCop
157
157
  return delimiter?(node.parent, char)
158
158
  end
159
159
 
160
+ return true unless node.loc.begin
161
+
160
162
  delimiters = [node.loc.begin.source[-1], node.loc.end.source[0]]
161
163
 
162
164
  delimiters.include?(char)
@@ -106,8 +106,9 @@ module RuboCop
106
106
  break unless sibling&.send_type? && sibling&.method?(node.method_name)
107
107
  break unless sibling.arguments? && !sibling.receiver
108
108
  break unless in_same_section?(sibling, node)
109
+ break unless node.first_argument.str_type? && sibling.first_argument.str_type?
109
110
 
110
- node.first_argument.source < sibling.first_argument.source
111
+ node.first_argument.value < sibling.first_argument.value
111
112
  end
112
113
  end
113
114
 
@@ -3,9 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of rescue in its modifier form.
7
- #
8
- # The cop to check `rescue` in its modifier form is added for following
6
+ # Checks for uses of `rescue` in its modifier form is added for following
9
7
  # reasons:
10
8
  #
11
9
  # * The syntax of modifier form `rescue` can be misleading because it