rubocop 0.76.0 → 0.79.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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +290 -264
  4. data/lib/rubocop/ast/builder.rb +43 -42
  5. data/lib/rubocop/ast/node/block_node.rb +2 -0
  6. data/lib/rubocop/ast/node/def_node.rb +11 -0
  7. data/lib/rubocop/ast/node/forward_args_node.rb +18 -0
  8. data/lib/rubocop/ast/node.rb +1 -1
  9. data/lib/rubocop/ast/traversal.rb +11 -3
  10. data/lib/rubocop/cli/command/auto_genenerate_config.rb +105 -0
  11. data/lib/rubocop/cli/command/base.rb +33 -0
  12. data/lib/rubocop/cli/command/execute_runner.rb +76 -0
  13. data/lib/rubocop/cli/command/init_dotfile.rb +45 -0
  14. data/lib/rubocop/cli/command/show_cops.rb +80 -0
  15. data/lib/rubocop/cli/command/version.rb +17 -0
  16. data/lib/rubocop/cli/command.rb +21 -0
  17. data/lib/rubocop/cli/environment.rb +21 -0
  18. data/lib/rubocop/cli.rb +11 -230
  19. data/lib/rubocop/config.rb +1 -1
  20. data/lib/rubocop/config_loader.rb +19 -19
  21. data/lib/rubocop/config_obsoletion.rb +65 -12
  22. data/lib/rubocop/config_validator.rb +56 -98
  23. data/lib/rubocop/cop/autocorrect_logic.rb +7 -4
  24. data/lib/rubocop/cop/bundler/gem_comment.rb +4 -4
  25. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +2 -2
  26. data/lib/rubocop/cop/cop.rb +21 -0
  27. data/lib/rubocop/cop/correctors/space_corrector.rb +1 -2
  28. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  29. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  30. data/lib/rubocop/cop/generator.rb +3 -4
  31. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +59 -0
  32. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  33. data/lib/rubocop/cop/layout/{align_arguments.rb → argument_alignment.rb} +1 -1
  34. data/lib/rubocop/cop/layout/{align_array.rb → array_alignment.rb} +1 -1
  35. data/lib/rubocop/cop/layout/{indent_assignment.rb → assignment_indentation.rb} +1 -1
  36. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  37. data/lib/rubocop/cop/layout/{indent_first_argument.rb → first_argument_indentation.rb} +5 -5
  38. data/lib/rubocop/cop/layout/{indent_first_array_element.rb → first_array_element_indentation.rb} +4 -4
  39. data/lib/rubocop/cop/layout/{indent_first_hash_element.rb → first_hash_element_indentation.rb} +3 -3
  40. data/lib/rubocop/cop/layout/{indent_first_parameter.rb → first_parameter_indentation.rb} +3 -3
  41. data/lib/rubocop/cop/layout/{align_hash.rb → hash_alignment.rb} +10 -6
  42. data/lib/rubocop/cop/layout/{indent_heredoc.rb → heredoc_indentation.rb} +5 -5
  43. data/lib/rubocop/cop/layout/{leading_blank_lines.rb → leading_empty_lines.rb} +1 -1
  44. data/lib/rubocop/cop/{metrics → layout}/line_length.rb +40 -110
  45. data/lib/rubocop/cop/layout/multiline_block_layout.rb +14 -5
  46. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +1 -1
  47. data/lib/rubocop/cop/layout/{align_parameters.rb → parameter_alignment.rb} +1 -1
  48. data/lib/rubocop/cop/layout/space_around_keyword.rb +12 -0
  49. data/lib/rubocop/cop/layout/space_around_operators.rb +32 -7
  50. data/lib/rubocop/cop/layout/space_before_block_braces.rb +17 -0
  51. data/lib/rubocop/cop/layout/{trailing_blank_lines.rb → trailing_empty_lines.rb} +1 -1
  52. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  53. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +1 -1
  54. data/lib/rubocop/cop/lint/{duplicated_key.rb → duplicate_hash_key.rb} +1 -1
  55. data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
  56. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -1
  57. data/lib/rubocop/cop/lint/{multiple_compare.rb → multiple_comparison.rb} +1 -1
  58. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +89 -0
  59. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -3
  60. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +4 -4
  61. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
  62. data/lib/rubocop/cop/lint/{string_conversion_in_interpolation.rb → redundant_string_coercion.rb} +1 -1
  63. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  64. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  65. data/lib/rubocop/cop/lint/{handle_exceptions.rb → suppressed_exception.rb} +1 -1
  66. data/lib/rubocop/cop/lint/useless_access_modifier.rb +57 -23
  67. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -1
  68. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  69. data/lib/rubocop/cop/migration/department_name.rb +16 -1
  70. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  71. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -7
  72. data/lib/rubocop/cop/mixin/{hash_alignment.rb → hash_alignment_styles.rb} +1 -1
  73. data/lib/rubocop/cop/mixin/line_length_help.rb +88 -0
  74. data/lib/rubocop/cop/mixin/nil_methods.rb +4 -4
  75. data/lib/rubocop/cop/mixin/rational_literal.rb +18 -0
  76. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
  77. data/lib/rubocop/cop/mixin/trailing_comma.rb +6 -3
  78. data/lib/rubocop/cop/naming/{uncommunicative_block_param_name.rb → block_parameter_name.rb} +3 -3
  79. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -5
  80. data/lib/rubocop/cop/naming/{uncommunicative_method_param_name.rb → method_parameter_name.rb} +4 -4
  81. data/lib/rubocop/cop/naming/predicate_name.rb +6 -6
  82. data/lib/rubocop/cop/offense.rb +11 -0
  83. data/lib/rubocop/cop/registry.rb +7 -2
  84. data/lib/rubocop/cop/style/alias.rb +1 -1
  85. data/lib/rubocop/cop/style/array_join.rb +1 -1
  86. data/lib/rubocop/cop/style/attr.rb +8 -0
  87. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  88. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  89. data/lib/rubocop/cop/style/even_odd.rb +1 -1
  90. data/lib/rubocop/cop/style/guard_clause.rb +3 -2
  91. data/lib/rubocop/cop/style/if_unless_modifier.rb +38 -3
  92. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  93. data/lib/rubocop/cop/style/ip_addresses.rb +4 -4
  94. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +168 -0
  95. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +54 -0
  96. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +4 -207
  97. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  98. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
  99. data/lib/rubocop/cop/style/multiline_when_then.rb +5 -1
  100. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +5 -5
  101. data/lib/rubocop/cop/style/next.rb +5 -5
  102. data/lib/rubocop/cop/style/numeric_literals.rb +7 -3
  103. data/lib/rubocop/cop/style/numeric_predicate.rb +4 -3
  104. data/lib/rubocop/cop/style/option_hash.rb +3 -3
  105. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +7 -7
  106. data/lib/rubocop/cop/style/redundant_parentheses.rb +3 -3
  107. data/lib/rubocop/cop/style/redundant_return.rb +2 -8
  108. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  109. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -1
  110. data/lib/rubocop/cop/style/trivial_accessors.rb +5 -5
  111. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  112. data/lib/rubocop/cop/style/yoda_condition.rb +16 -1
  113. data/lib/rubocop/cop/team.rb +5 -0
  114. data/lib/rubocop/formatter/base_formatter.rb +2 -2
  115. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -3
  116. data/lib/rubocop/formatter/json_formatter.rb +6 -5
  117. data/lib/rubocop/formatter/tap_formatter.rb +1 -3
  118. data/lib/rubocop/node_pattern.rb +1 -1
  119. data/lib/rubocop/options.rb +8 -8
  120. data/lib/rubocop/processed_source.rb +1 -1
  121. data/lib/rubocop/rake_task.rb +1 -0
  122. data/lib/rubocop/result_cache.rb +23 -7
  123. data/lib/rubocop/rspec/shared_contexts.rb +5 -0
  124. data/lib/rubocop/runner.rb +18 -2
  125. data/lib/rubocop/target_ruby.rb +151 -0
  126. data/lib/rubocop/version.rb +1 -1
  127. data/lib/rubocop.rb +38 -22
  128. metadata +40 -25
  129. data/lib/rubocop/cop/mixin/safe_mode.rb +0 -24
@@ -8,9 +8,9 @@ module RuboCop
8
8
  # In Ruby 2.3 or newer, squiggly heredocs (`<<~`) should be used. If you
9
9
  # use the older rubies, you should introduce some library to your project
10
10
  # (e.g. ActiveSupport, Powerpack or Unindent).
11
- # Note: When `Metrics/LineLength`'s `AllowHeredoc` is false (not default),
11
+ # Note: When `Layout/LineLength`'s `AllowHeredoc` is false (not default),
12
12
  # this cop does not add any offenses for long here documents to
13
- # avoid `Metrics/LineLength`'s offenses.
13
+ # avoid `Layout/LineLength`'s offenses.
14
14
  #
15
15
  # @example EnforcedStyle: squiggly (default)
16
16
  # # bad
@@ -49,7 +49,7 @@ module RuboCop
49
49
  # something
50
50
  # RUBY
51
51
  #
52
- class IndentHeredoc < Cop
52
+ class HeredocIndentation < Cop
53
53
  include Heredoc
54
54
  include ConfigurableEnforcedStyle
55
55
 
@@ -159,11 +159,11 @@ module RuboCop
159
159
  end
160
160
 
161
161
  def unlimited_heredoc_length?
162
- config.for_cop('Metrics/LineLength')['AllowHeredoc']
162
+ config.for_cop('Layout/LineLength')['AllowHeredoc']
163
163
  end
164
164
 
165
165
  def max_line_length
166
- config.for_cop('Metrics/LineLength')['Max']
166
+ config.for_cop('Layout/LineLength')['Max']
167
167
  end
168
168
 
169
169
  def correct_by_squiggly(node)
@@ -27,7 +27,7 @@ module RuboCop
27
27
  # # good
28
28
  # # (start of file)
29
29
  # # a comment
30
- class LeadingBlankLines < Cop
30
+ class LeadingEmptyLines < Cop
31
31
  MSG = 'Unnecessary blank line at the beginning of the source.'
32
32
 
33
33
  def investigate(processed_source)
@@ -2,10 +2,9 @@
2
2
 
3
3
  require 'uri'
4
4
 
5
- # rubocop:disable Metrics/ClassLength
6
5
  module RuboCop
7
6
  module Cop
8
- module Metrics
7
+ module Layout
9
8
  # This cop checks the length of lines in the source code.
10
9
  # The maximum length is configurable.
11
10
  # The tab size is configured in the `IndentationWidth`
@@ -21,14 +20,14 @@ module RuboCop
21
20
  # If autocorrection is enabled, the following Layout cops
22
21
  # are recommended to further format the broken lines.
23
22
  #
24
- # - AlignArray
25
- # - AlignHash
26
- # - AlignParameters
23
+ # - ParameterAlignment
24
+ # - ArgumentAlignment
27
25
  # - ClosingParenthesisIndentation
28
- # - IndentFirstArgument
29
- # - IndentFirstArrayElement
30
- # - IndentFirstHashElement
31
- # - IndentFirstParameter
26
+ # - FirstArgumentIndentation
27
+ # - FirstArrayElementIndentation
28
+ # - FirstHashElementIndentation
29
+ # - FirstParameterIndentation
30
+ # - HashAlignment
32
31
  # - MultilineArrayLineBreaks
33
32
  # - MultilineHashBraceLayout
34
33
  # - MultilineHashKeyLineBreaks
@@ -58,6 +57,7 @@ module RuboCop
58
57
  include ConfigurableMax
59
58
  include IgnoredPattern
60
59
  include RangeHelp
60
+ include LineLengthHelp
61
61
 
62
62
  MSG = 'Line is too long. [%<length>d/%<max>d]'
63
63
 
@@ -68,6 +68,10 @@ module RuboCop
68
68
  alias on_hash on_potential_breakable_node
69
69
  alias on_send on_potential_breakable_node
70
70
 
71
+ def investigate(processed_source)
72
+ check_for_breakable_semicolons(processed_source)
73
+ end
74
+
71
75
  def investigate_post_walk(processed_source)
72
76
  processed_source.lines.each_with_index do |line, line_index|
73
77
  check_line(line, line_index)
@@ -89,29 +93,41 @@ module RuboCop
89
93
  return if breakable_node.nil?
90
94
 
91
95
  line_index = breakable_node.first_line - 1
92
- breakable_nodes_by_line_index[line_index] = breakable_node
93
- end
96
+ range = breakable_node.source_range
94
97
 
95
- def breakable_nodes_by_line_index
96
- @breakable_nodes_by_line_index ||= {}
97
- end
98
+ existing = breakable_range_by_line_index[line_index]
99
+ return if existing
98
100
 
99
- def heredocs
100
- @heredocs ||= extract_heredocs(processed_source.ast)
101
+ breakable_range_by_line_index[line_index] = range
101
102
  end
102
103
 
103
- def tab_indentation_width
104
- config.for_cop('Layout/Tab')['IndentationWidth']
104
+ def check_for_breakable_semicolons(processed_source)
105
+ tokens = processed_source.tokens.select { |t| t.type == :tSEMI }
106
+ tokens.reverse_each do |token|
107
+ range = breakable_range_after_semicolon(token)
108
+ breakable_range_by_line_index[range.line - 1] = range if range
109
+ end
105
110
  end
106
111
 
107
- def indentation_difference(line)
108
- return 0 unless tab_indentation_width
112
+ def breakable_range_after_semicolon(semicolon_token)
113
+ range = semicolon_token.pos
114
+ end_pos = range.end_pos
115
+ next_range = range_between(end_pos, end_pos + 1)
116
+ return nil unless next_range.line == range.line
117
+
118
+ next_char = next_range.source
119
+ return nil if /[\r\n]/ =~ next_char
120
+ return nil if next_char == ';'
109
121
 
110
- line.match(/^\t*/)[0].size * (tab_indentation_width - 1)
122
+ next_range
111
123
  end
112
124
 
113
- def line_length(line)
114
- line.length + indentation_difference(line)
125
+ def breakable_range_by_line_index
126
+ @breakable_range_by_line_index ||= {}
127
+ end
128
+
129
+ def heredocs
130
+ @heredocs ||= extract_heredocs(processed_source.ast)
115
131
  end
116
132
 
117
133
  def highlight_start(line)
@@ -147,33 +163,12 @@ module RuboCop
147
163
  def register_offense(loc, line, line_index)
148
164
  message = format(MSG, length: line_length(line), max: max)
149
165
 
150
- breakable_range = breakable_range(line, line_index)
166
+ breakable_range = breakable_range_by_line_index[line_index]
151
167
  add_offense(breakable_range, location: loc, message: message) do
152
168
  self.max = line_length(line)
153
169
  end
154
170
  end
155
171
 
156
- def breakable_range(line, line_index)
157
- return if line_in_heredoc?(line_index + 1)
158
-
159
- semicolon_range = breakable_semicolon_range(line, line_index)
160
- return semicolon_range if semicolon_range
161
-
162
- breakable_node = breakable_nodes_by_line_index[line_index]
163
- return breakable_node.source_range if breakable_node
164
- end
165
-
166
- def breakable_semicolon_range(line, line_index)
167
- semicolon_separated_parts = line.split(';')
168
- return if semicolon_separated_parts.length <= 1
169
-
170
- column = semicolon_separated_parts.first.length + 1
171
- range = source_range(processed_source.buffer, line_index, column, 1)
172
- return if processed_source.commented?(range)
173
-
174
- range
175
- end
176
-
177
172
  def excess_range(uri_range, line, line_index)
178
173
  excessive_position = if uri_range && uri_range.begin < max
179
174
  uri_range.end
@@ -222,53 +217,6 @@ module RuboCop
222
217
  end
223
218
  end
224
219
 
225
- def allow_uri?
226
- cop_config['AllowURI']
227
- end
228
-
229
- def ignore_cop_directives?
230
- cop_config['IgnoreCopDirectives']
231
- end
232
-
233
- def allowed_uri_position?(line, uri_range)
234
- uri_range.begin < max &&
235
- (uri_range.end == line_length(line) ||
236
- uri_range.end == line_length(line) - 1)
237
- end
238
-
239
- def find_excessive_uri_range(line)
240
- last_uri_match = match_uris(line).last
241
- return nil unless last_uri_match
242
-
243
- begin_position, end_position =
244
- last_uri_match.offset(0).map do |pos|
245
- pos + indentation_difference(line)
246
- end
247
- return nil if begin_position < max && end_position < max
248
-
249
- begin_position...end_position
250
- end
251
-
252
- def match_uris(string)
253
- matches = []
254
- string.scan(uri_regexp) do
255
- matches << $LAST_MATCH_INFO if valid_uri?($LAST_MATCH_INFO[0])
256
- end
257
- matches
258
- end
259
-
260
- def valid_uri?(uri_ish_string)
261
- URI.parse(uri_ish_string)
262
- true
263
- rescue URI::InvalidURIError, NoMethodError
264
- false
265
- end
266
-
267
- def uri_regexp
268
- @uri_regexp ||=
269
- URI::DEFAULT_PARSER.make_regexp(cop_config['URISchemes'])
270
- end
271
-
272
220
  def check_directive_line(line, line_index)
273
221
  return if line_length_without_directive(line) <= max
274
222
 
@@ -284,23 +232,6 @@ module RuboCop
284
232
  )
285
233
  end
286
234
 
287
- def directive_on_source_line?(line_index)
288
- source_line_number = line_index + processed_source.buffer.first_line
289
- comment =
290
- processed_source
291
- .comments
292
- .detect { |e| e.location.line == source_line_number }
293
-
294
- return false unless comment
295
-
296
- comment.text.match(CommentConfig::COMMENT_DIRECTIVE_REGEXP)
297
- end
298
-
299
- def line_length_without_directive(line)
300
- before_comment, = line.split(CommentConfig::COMMENT_DIRECTIVE_REGEXP)
301
- before_comment.rstrip.length
302
- end
303
-
304
235
  def check_uri_line(line, line_index)
305
236
  uri_range = find_excessive_uri_range(line)
306
237
  return if uri_range && allowed_uri_position?(line, uri_range)
@@ -315,4 +246,3 @@ module RuboCop
315
246
  end
316
247
  end
317
248
  end
318
- # rubocop:enable Metrics/ClassLength
@@ -97,7 +97,8 @@ module RuboCop
97
97
  def line_break_necessary_in_args?(node)
98
98
  needed_length = node.source_range.column +
99
99
  node.source.lines.first.length +
100
- block_arg_string(node.arguments).length + PIPE_SIZE
100
+ block_arg_string(node, node.arguments).length +
101
+ PIPE_SIZE
101
102
  needed_length > max_line_length
102
103
  end
103
104
 
@@ -115,7 +116,8 @@ module RuboCop
115
116
  newlines: false
116
117
  ).end_pos
117
118
  range = range_between(node.loc.begin.end.begin_pos, end_pos)
118
- corrector.replace(range, " |#{block_arg_string(node.arguments)}|")
119
+ corrector.replace(range,
120
+ " |#{block_arg_string(node, node.arguments)}|")
119
121
  end
120
122
 
121
123
  def autocorrect_body(corrector, node, block_body)
@@ -131,14 +133,21 @@ module RuboCop
131
133
  "\n #{' ' * block_start_col}")
132
134
  end
133
135
 
134
- def block_arg_string(args)
135
- args.children.map do |arg|
136
+ def block_arg_string(node, args)
137
+ arg_string = args.children.map do |arg|
136
138
  if arg.mlhs_type?
137
- "(#{block_arg_string(arg)})"
139
+ "(#{block_arg_string(node, arg)})"
138
140
  else
139
141
  arg.source
140
142
  end
141
143
  end.join(', ')
144
+ arg_string += ',' if include_trailing_comma?(node.arguments)
145
+ arg_string
146
+ end
147
+
148
+ def include_trailing_comma?(args)
149
+ arg_count = args.each_descendant(:arg).to_a.size
150
+ arg_count == 1 && args.source.include?(',')
142
151
  end
143
152
  end
144
153
  end
@@ -26,7 +26,7 @@ module RuboCop
26
26
  'on a separate line.'
27
27
 
28
28
  def on_send(node)
29
- return if node.method_name == :[]=
29
+ return if node.method?(:[]=)
30
30
 
31
31
  args = node.arguments
32
32
 
@@ -68,7 +68,7 @@ module RuboCop
68
68
  # baz)
69
69
  # 123
70
70
  # end
71
- class AlignParameters < Cop
71
+ class ParameterAlignment < Cop
72
72
  include Alignment
73
73
 
74
74
  ALIGN_PARAMS_MSG = 'Align the parameters of a method definition if ' \
@@ -30,10 +30,12 @@ module RuboCop
30
30
 
31
31
  DO = 'do'
32
32
  SAFE_NAVIGATION = '&.'
33
+ NAMESPACE_OPERATOR = '::'
33
34
  ACCEPT_LEFT_PAREN =
34
35
  %w[break defined? next not rescue return super yield].freeze
35
36
  ACCEPT_LEFT_SQUARE_BRACKET =
36
37
  %w[super yield].freeze
38
+ ACCEPT_NAMESPACE_OPERATOR = 'super'
37
39
 
38
40
  def on_and(node)
39
41
  check(node, [:operator].freeze) if node.keyword?
@@ -193,6 +195,8 @@ module RuboCop
193
195
 
194
196
  return false if accepted_opening_delimiter?(range, char)
195
197
  return false if safe_navigation_call?(range, pos)
198
+ return false if accept_namespace_operator?(range) &&
199
+ namespace_operator?(range, pos)
196
200
 
197
201
  char !~ /[\s;,#\\\)\}\]\.]/
198
202
  end
@@ -212,10 +216,18 @@ module RuboCop
212
216
  ACCEPT_LEFT_SQUARE_BRACKET.include?(range.source)
213
217
  end
214
218
 
219
+ def accept_namespace_operator?(range)
220
+ ACCEPT_NAMESPACE_OPERATOR == range.source
221
+ end
222
+
215
223
  def safe_navigation_call?(range, pos)
216
224
  range.source_buffer.source[pos, 2].start_with?(SAFE_NAVIGATION)
217
225
  end
218
226
 
227
+ def namespace_operator?(range, pos)
228
+ range.source_buffer.source[pos, 2].start_with?(NAMESPACE_OPERATOR)
229
+ end
230
+
219
231
  def preceded_by_operator?(node, _range)
220
232
  # regular dotted method calls bind more tightly than operators
221
233
  # so we need to climb up the AST past them
@@ -3,24 +3,37 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Layout
6
- # Checks that operators have space around them, except for **
7
- # which should not have surrounding space.
6
+ # Checks that operators have space around them, except for ** which
7
+ # should or shouldn't have surrounding space depending on configuration.
8
8
  #
9
9
  # @example
10
10
  # # bad
11
11
  # total = 3*4
12
12
  # "apple"+"juice"
13
13
  # my_number = 38/4
14
- # a ** b
15
14
  #
16
15
  # # good
17
16
  # total = 3 * 4
18
17
  # "apple" + "juice"
19
18
  # my_number = 38 / 4
19
+ #
20
+ # @example EnforcedStyleForExponentOperator: no_space (default)
21
+ # # bad
22
+ # a ** b
23
+ #
24
+ # # good
25
+ # a**b
26
+ #
27
+ # @example EnforcedStyleForExponentOperator: space
28
+ # # bad
20
29
  # a**b
30
+ #
31
+ # # good
32
+ # a ** b
21
33
  class SpaceAroundOperators < Cop
22
34
  include PrecedingFollowingAlignment
23
35
  include RangeHelp
36
+ include RationalLiteral
24
37
 
25
38
  IRREGULAR_METHODS = %i[[] ! []=].freeze
26
39
  EXCESSIVE_SPACE = ' '
@@ -53,6 +66,8 @@ module RuboCop
53
66
  end
54
67
 
55
68
  def on_send(node)
69
+ return if rational_literal?(node)
70
+
56
71
  if node.setter_method?
57
72
  on_special_asgn(node)
58
73
  elsif regular_operator?(node)
@@ -101,7 +116,7 @@ module RuboCop
101
116
 
102
117
  def autocorrect(range)
103
118
  lambda do |corrector|
104
- if range.source =~ /\*\*/
119
+ if range.source =~ /\*\*/ && !space_around_exponent_operator?
105
120
  corrector.replace(range, '**')
106
121
  elsif range.source.end_with?("\n")
107
122
  corrector.replace(range, " #{range.source.strip}\n")
@@ -138,8 +153,10 @@ module RuboCop
138
153
  end
139
154
 
140
155
  def offense_message(type, operator, with_space, right_operand)
141
- if operator.is?('**')
142
- 'Space around operator `**` detected.' unless with_space.is?('**')
156
+ if should_not_have_surrounding_space?(operator)
157
+ return if with_space.is?(operator.source)
158
+
159
+ "Space around operator `#{operator.source}` detected."
143
160
  elsif with_space.source !~ /^\s.*\s$/
144
161
  "Surrounding space missing for operator `#{operator.source}`."
145
162
  elsif excess_leading_space?(type, operator, with_space) ||
@@ -169,13 +186,21 @@ module RuboCop
169
186
  end
170
187
 
171
188
  def align_hash_cop_config
172
- config.for_cop('Layout/AlignHash')
189
+ config.for_cop('Layout/HashAlignment')
173
190
  end
174
191
 
175
192
  def hash_table_style?
176
193
  align_hash_cop_config &&
177
194
  align_hash_cop_config['EnforcedHashRocketStyle'] == 'table'
178
195
  end
196
+
197
+ def space_around_exponent_operator?
198
+ cop_config['EnforcedStyleForExponentOperator'] == 'space'
199
+ end
200
+
201
+ def should_not_have_surrounding_space?(operator)
202
+ operator.is?('**') ? !space_around_exponent_operator? : false
203
+ end
179
204
  end
180
205
  end
181
206
  end
@@ -41,6 +41,14 @@ module RuboCop
41
41
  def on_block(node)
42
42
  return if node.keywords?
43
43
 
44
+ # Do not register an offense for multi-line braces when specifying
45
+ # `EnforcedStyle: no_space`. It will conflict with auto-correction
46
+ # by `EnforcedStyle: line_count_based` of `Style/BlockDelimiters` cop.
47
+ # That means preventing auto-correction to incorrect auto-corrected
48
+ # code.
49
+ # See: https://github.com/rubocop-hq/rubocop/issues/7534
50
+ return if conflict_with_block_delimiters?(node)
51
+
44
52
  left_brace = node.loc.begin
45
53
  space_plus_brace = range_with_surrounding_space(range: left_brace)
46
54
  used_style =
@@ -110,6 +118,15 @@ module RuboCop
110
118
  end
111
119
  end
112
120
 
121
+ def conflict_with_block_delimiters?(node)
122
+ block_delimiters_style == 'line_count_based' &&
123
+ style == :no_space && node.multiline?
124
+ end
125
+
126
+ def block_delimiters_style
127
+ config.for_cop('Style/BlockDelimiters')['EnforcedStyle']
128
+ end
129
+
113
130
  def empty_braces?(loc)
114
131
  loc.begin.end_pos == loc.end.begin_pos
115
132
  end
@@ -37,7 +37,7 @@ module RuboCop
37
37
  # class Foo; end
38
38
  # # EOF
39
39
  #
40
- class TrailingBlankLines < Cop
40
+ class TrailingEmptyLines < Cop
41
41
  include ConfigurableEnforcedStyle
42
42
  include RangeHelp
43
43
 
@@ -43,9 +43,9 @@ module RuboCop
43
43
  PATTERN
44
44
 
45
45
  def_node_matcher :debugger_call?, <<~PATTERN
46
- {(send {nil? #kernel?} {:debugger :byebug :remote_byebug} ...)
46
+ {(send {nil? #kernel?} {:debugger :byebug :remote_byebug :console} ...)
47
47
  (send (send {#kernel? nil?} :binding)
48
- {:pry :remote_pry :pry_remote} ...)
48
+ {:pry :remote_pry :pry_remote :console} ...)
49
49
  (send (const {nil? (cbase)} :Pry) :rescue ...)
50
50
  (send nil? {:save_and_open_page
51
51
  :save_and_open_screenshot
@@ -33,7 +33,7 @@ module RuboCop
33
33
 
34
34
  # @param [DefNode] node a constructor definition
35
35
  def check(node)
36
- return unless node.method_name == :initialize
36
+ return unless node.method?(:initialize)
37
37
 
38
38
  check_body(node.body)
39
39
  end
@@ -18,7 +18,7 @@ module RuboCop
18
18
  # # good
19
19
  #
20
20
  # hash = { food: 'apple', other_food: 'orange' }
21
- class DuplicatedKey < Cop
21
+ class DuplicateHashKey < Cop
22
22
  include Duplication
23
23
 
24
24
  MSG = 'Duplicated key in hash literal.'
@@ -22,7 +22,7 @@ module RuboCop
22
22
  # num = 0
23
23
  # sum = numbers.each_with_object(num) { |e, a| a += e }
24
24
  class EachWithObjectArgument < Cop
25
- MSG = 'The argument to each_with_object can not be immutable.'
25
+ MSG = 'The argument to each_with_object cannot be immutable.'
26
26
 
27
27
  def_node_matcher :each_with_object?, <<~PATTERN
28
28
  ({send csend} _ :each_with_object $_)
@@ -117,7 +117,7 @@ module RuboCop
117
117
  end
118
118
 
119
119
  def correct_visibility?(node, modifier, ignored_methods)
120
- return true if modifier.nil? || modifier.method_name == :public
120
+ return true if modifier.nil? || modifier.method?(:public)
121
121
 
122
122
  ignored_methods.include?(node.method_name)
123
123
  end
@@ -21,7 +21,7 @@ module RuboCop
21
21
  #
22
22
  # x < y && y < z
23
23
  # 10 <= x && x <= 20
24
- class MultipleCompare < Cop
24
+ class MultipleComparison < Cop
25
25
  MSG = 'Use the `&&` operator to compare multiple values.'
26
26
 
27
27
  def_node_matcher :multiple_compare?, <<~PATTERN
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # `Dir[...]` and `Dir.glob(...)` do not make any guarantees about
7
+ # the order in which files are returned. The final order is
8
+ # determined by the operating system and file system.
9
+ # This means that using them in cases where the order matters,
10
+ # such as requiring files, can lead to intermittent failures
11
+ # that are hard to debug. To ensure this doesn't happen,
12
+ # always sort the list.
13
+ #
14
+ # @example
15
+ #
16
+ # # bad
17
+ # Dir["./lib/**/*.rb"].each do |file|
18
+ # require file
19
+ # end
20
+ #
21
+ # # good
22
+ # Dir["./lib/**/*.rb"].sort.each do |file|
23
+ # require file
24
+ # end
25
+ #
26
+ # @example
27
+ #
28
+ # # bad
29
+ # Dir.glob(Rails.root.join(__dir__, 'test', '*.rb')) do |file|
30
+ # require file
31
+ # end
32
+ #
33
+ # # good
34
+ # Dir.glob(Rails.root.join(__dir__, 'test', '*.rb')).sort.each do |file|
35
+ # require file
36
+ # end
37
+ #
38
+ class NonDeterministicRequireOrder < Cop
39
+ MSG = 'Sort files before requiring them.'
40
+
41
+ def on_block(node)
42
+ return unless node.body
43
+ return unless unsorted_dir_loop?(node.send_node)
44
+
45
+ loop_variable(node.arguments) do |var_name|
46
+ return unless var_is_required?(node.body, var_name)
47
+
48
+ add_offense(node.send_node)
49
+ end
50
+ end
51
+
52
+ def autocorrect(node)
53
+ if unsorted_dir_block?(node)
54
+ lambda do |corrector|
55
+ corrector.replace(node.loc.expression, "#{node.source}.sort.each")
56
+ end
57
+ else
58
+ lambda do |corrector|
59
+ source = node.receiver.source
60
+ corrector.replace(node.loc.expression, "#{source}.sort.each")
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def unsorted_dir_loop?(node)
68
+ unsorted_dir_block?(node) || unsorted_dir_each?(node)
69
+ end
70
+
71
+ def_node_matcher :unsorted_dir_block?, <<~PATTERN
72
+ (send (const nil? :Dir) :glob ...)
73
+ PATTERN
74
+
75
+ def_node_matcher :unsorted_dir_each?, <<~PATTERN
76
+ (send (send (const nil? :Dir) {:[] :glob} ...) :each)
77
+ PATTERN
78
+
79
+ def_node_matcher :loop_variable, <<~PATTERN
80
+ (args (arg $_))
81
+ PATTERN
82
+
83
+ def_node_search :var_is_required?, <<~PATTERN
84
+ (send nil? :require (lvar %1))
85
+ PATTERN
86
+ end
87
+ end
88
+ end
89
+ end