rubocop 1.86.2 → 1.88.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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +82 -71
  3. data/config/obsoletion.yml +21 -1
  4. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -0
  5. data/lib/rubocop/cli.rb +2 -0
  6. data/lib/rubocop/config_loader.rb +17 -2
  7. data/lib/rubocop/config_loader_resolver.rb +11 -3
  8. data/lib/rubocop/config_store.rb +1 -1
  9. data/lib/rubocop/cop/base.rb +25 -4
  10. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  11. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
  12. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  13. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  14. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  15. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  16. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  17. data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
  18. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  19. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +14 -5
  20. data/lib/rubocop/cop/layout/end_alignment.rb +2 -2
  21. data/lib/rubocop/cop/layout/indentation_width.rb +13 -1
  22. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  23. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  24. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
  25. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  26. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
  27. data/lib/rubocop/cop/lint/circular_argument_reference.rb +1 -3
  28. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  29. data/lib/rubocop/cop/lint/constant_reassignment.rb +36 -4
  30. data/lib/rubocop/cop/lint/constant_resolution.rb +5 -5
  31. data/lib/rubocop/cop/lint/debugger.rb +0 -1
  32. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  33. data/lib/rubocop/cop/lint/empty_block.rb +3 -3
  34. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  35. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
  36. data/lib/rubocop/cop/lint/float_comparison.rb +1 -0
  37. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  38. data/lib/rubocop/cop/lint/interpolation_check.rb +18 -3
  39. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  40. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  41. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -11
  42. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +5 -5
  43. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  44. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +16 -0
  45. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  46. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  47. data/lib/rubocop/cop/lint/number_conversion.rb +18 -9
  48. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  49. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
  50. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  51. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  52. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  53. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  54. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -1
  55. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -3
  56. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
  57. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -7
  58. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +4 -0
  59. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
  60. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
  61. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  62. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  63. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  64. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  65. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  66. data/lib/rubocop/cop/lint/require_relative_self_path.rb +6 -6
  67. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  68. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -0
  69. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  70. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  71. data/lib/rubocop/cop/lint/self_assignment.rb +24 -1
  72. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  73. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  74. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  75. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  76. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
  77. data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
  78. data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
  79. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  80. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +4 -1
  81. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +4 -2
  82. data/lib/rubocop/cop/lint/unreachable_code.rb +2 -2
  83. data/lib/rubocop/cop/lint/useless_assignment.rb +10 -5
  84. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
  85. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
  86. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  87. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  88. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  89. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  90. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  91. data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
  92. data/lib/rubocop/cop/mixin.rb +1 -0
  93. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  94. data/lib/rubocop/cop/naming/predicate_method.rb +2 -2
  95. data/lib/rubocop/cop/naming/predicate_prefix.rb +1 -1
  96. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  97. data/lib/rubocop/cop/registry.rb +28 -6
  98. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  99. data/lib/rubocop/cop/style/alias.rb +11 -2
  100. data/lib/rubocop/cop/style/and_or.rb +1 -1
  101. data/lib/rubocop/cop/style/array_first_last.rb +12 -1
  102. data/lib/rubocop/cop/style/array_intersect.rb +4 -0
  103. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
  104. data/lib/rubocop/cop/style/block_delimiters.rb +16 -2
  105. data/lib/rubocop/cop/style/case_equality.rb +14 -2
  106. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  107. data/lib/rubocop/cop/style/class_and_module_children.rb +8 -0
  108. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  109. data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
  110. data/lib/rubocop/cop/style/colon_method_call.rb +13 -6
  111. data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
  112. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  113. data/lib/rubocop/cop/style/concat_array_literals.rb +5 -1
  114. data/lib/rubocop/cop/style/conditional_assignment.rb +6 -1
  115. data/lib/rubocop/cop/style/constant_visibility.rb +4 -1
  116. data/lib/rubocop/cop/style/date_time.rb +2 -2
  117. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  118. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  119. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  120. data/lib/rubocop/cop/style/file_write.rb +21 -16
  121. data/lib/rubocop/cop/style/format_string.rb +4 -3
  122. data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
  123. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  124. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
  125. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  126. data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
  127. data/lib/rubocop/cop/style/mutable_constant.rb +105 -11
  128. data/lib/rubocop/cop/style/parallel_assignment.rb +8 -1
  129. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  130. data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
  131. data/lib/rubocop/cop/style/redundant_format.rb +1 -0
  132. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
  133. data/lib/rubocop/cop/style/regexp_literal.rb +2 -2
  134. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
  135. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  136. data/lib/rubocop/cop/style/semicolon.rb +16 -1
  137. data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
  138. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  139. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  140. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  141. data/lib/rubocop/cop/style/word_array.rb +1 -0
  142. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  143. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  144. data/lib/rubocop/file_patterns.rb +9 -1
  145. data/lib/rubocop/formatter/disabled_config_formatter.rb +14 -7
  146. data/lib/rubocop/options.rb +18 -0
  147. data/lib/rubocop/project_index_loader.rb +66 -0
  148. data/lib/rubocop/runner.rb +47 -3
  149. data/lib/rubocop/server/core.rb +6 -0
  150. data/lib/rubocop/version.rb +20 -2
  151. data/lib/rubocop.rb +1 -0
  152. metadata +5 -3
@@ -24,7 +24,7 @@ module RuboCop
24
24
  include FileFinder
25
25
 
26
26
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
27
- :ignore_unrecognized_cops
27
+ :enabled_by_default, :disabled_by_default, :ignore_unrecognized_cops
28
28
  attr_writer :default_configuration, :cache_root
29
29
  attr_reader :loaded_plugins, :loaded_features
30
30
 
@@ -41,6 +41,8 @@ module RuboCop
41
41
  @loaded_features = Set.new
42
42
  @disable_pending_cops = nil
43
43
  @enable_pending_cops = nil
44
+ @enabled_by_default = nil
45
+ @disabled_by_default = nil
44
46
  @ignore_parent_exclusion = nil
45
47
  @ignore_unrecognized_cops = nil
46
48
  @cache_root = nil
@@ -121,7 +123,7 @@ module RuboCop
121
123
  end
122
124
 
123
125
  def configuration_from_file(config_file, check: true)
124
- return default_configuration if config_file == DEFAULT_FILE
126
+ return apply_default_overrides(default_configuration) if config_file == DEFAULT_FILE
125
127
 
126
128
  config = load_file(config_file, check: check)
127
129
  config.validate_after_resolution if check
@@ -190,6 +192,19 @@ module RuboCop
190
192
  resolver.merge_with_default(config, config_file, unset_nil: unset_nil)
191
193
  end
192
194
 
195
+ # Applies CLI overrides for `AllCops/EnabledByDefault` and
196
+ # `AllCops/DisabledByDefault` to the given configuration. Used when the
197
+ # configuration would otherwise be returned without going through
198
+ # `merge_with_default` (e.g. there is no user-supplied `.rubocop.yml`).
199
+ def apply_default_overrides(config)
200
+ return config if @enabled_by_default.nil? && @disabled_by_default.nil?
201
+
202
+ hash = config.transform_values do |params|
203
+ params.is_a?(Hash) ? params.merge('Enabled' => !@disabled_by_default) : params
204
+ end
205
+ Config.new(hash, config.loaded_path)
206
+ end
207
+
193
208
  # @api private
194
209
  # Used to add plugins that were required inside a config or from
195
210
  # the CLI using `--plugin`.
@@ -92,11 +92,11 @@ module RuboCop
92
92
  # only cops from user configuration are enabled. If
93
93
  # AllCops:EnabledByDefault is true, it changes the Enabled params so that
94
94
  # only cops explicitly disabled in user configuration are disabled.
95
+ # When the `--disable-all-cops` or `--enable-all-cops` CLI option is given,
96
+ # it takes precedence over the configuration values.
95
97
  def merge_with_default(config, config_file, unset_nil:)
96
98
  default_configuration = ConfigLoader.default_configuration
97
-
98
- disabled_by_default = config.for_all_cops['DisabledByDefault']
99
- enabled_by_default = config.for_all_cops['EnabledByDefault']
99
+ disabled_by_default, enabled_by_default = resolve_default_overrides(config)
100
100
 
101
101
  if disabled_by_default || enabled_by_default
102
102
  default_configuration = transform(default_configuration) do |params|
@@ -169,6 +169,14 @@ module RuboCop
169
169
 
170
170
  private
171
171
 
172
+ def resolve_default_overrides(config)
173
+ if ConfigLoader.disabled_by_default || ConfigLoader.enabled_by_default
174
+ [ConfigLoader.disabled_by_default, ConfigLoader.enabled_by_default]
175
+ else
176
+ [config.for_all_cops['DisabledByDefault'], config.for_all_cops['EnabledByDefault']]
177
+ end
178
+ end
179
+
172
180
  def disabled?(hash, department)
173
181
  hash[department].is_a?(Hash) && hash[department]['Enabled'] == false
174
182
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  end
37
37
 
38
38
  def force_default_config!
39
- @options_config = ConfigLoader.default_configuration
39
+ @options_config = ConfigLoader.apply_default_overrides(ConfigLoader.default_configuration)
40
40
  end
41
41
 
42
42
  def unvalidated
@@ -41,6 +41,7 @@ module RuboCop
41
41
  include AutocorrectLogic
42
42
 
43
43
  attr_reader :config, :processed_source
44
+ attr_accessor :project_index
44
45
 
45
46
  # Reports of an investigation.
46
47
  # Immutable
@@ -306,7 +307,9 @@ module RuboCop
306
307
  def ready
307
308
  return self if self.class.support_multiple_source?
308
309
 
309
- self.class.new(@config, @options)
310
+ self.class.new(@config, @options).tap do |fresh|
311
+ fresh.project_index = @project_index
312
+ end
310
313
  end
311
314
 
312
315
  ### Reserved for Cop::Cop
@@ -416,7 +419,10 @@ module RuboCop
416
419
  ### Actually private methods
417
420
 
418
421
  def reset_investigation
419
- @currently_disabled_lines = @current_offenses = @processed_source = @current_corrector = nil
422
+ @currently_disabled_lines = nil
423
+ @current_offenses = nil
424
+ @processed_source = nil
425
+ @current_corrector = nil
420
426
  end
421
427
 
422
428
  # @return [Symbol, Corrector] offense status
@@ -492,8 +498,23 @@ module RuboCop
492
498
  patterns = cop_config[parameter]
493
499
  return default_result unless patterns
494
500
 
495
- patterns = FilePatterns.from(patterns)
496
- patterns.match?(config.path_relative_to_config(file)) || patterns.match?(file)
501
+ file_patterns = FilePatterns.from(patterns)
502
+ relative_file_path = config.path_relative_to_config(file)
503
+ return true if file_patterns.match?(relative_file_path)
504
+
505
+ if parameter == 'Include' && !relative_file_path.start_with?('..')
506
+ matches_absolute_include_pattern?(patterns, file)
507
+ else
508
+ file_patterns.match?(file)
509
+ end
510
+ end
511
+
512
+ def matches_absolute_include_pattern?(patterns, file)
513
+ absolute_file_path = absolute?(file) ? file : File.expand_path(file)
514
+ patterns.any? do |pattern|
515
+ (absolute?(pattern.to_s) || pattern.to_s.start_with?('..')) &&
516
+ match_path?(pattern, absolute_file_path)
517
+ end
497
518
  end
498
519
 
499
520
  def enabled_line?(line_number)
@@ -128,8 +128,8 @@ module RuboCop
128
128
  end
129
129
 
130
130
  def ignored_gem?(node)
131
- ignored_gems = Array(cop_config['IgnoredGems'])
132
- ignored_gems.include?(node.first_argument.value)
131
+ allowed_gems = Array(cop_config['AllowedGems'])
132
+ allowed_gems.include?(node.first_argument.value)
133
133
  end
134
134
 
135
135
  def checked_options_present?(node)
@@ -13,8 +13,7 @@ module RuboCop
13
13
  buffer = node.source_range.source_buffer
14
14
  corrector.remove(range_with_surrounding_space(range: node.loc.begin, buffer: buffer,
15
15
  side: :right, whitespace: true))
16
- corrector.remove(range_with_surrounding_space(range: node.loc.end, buffer: buffer,
17
- side: :left))
16
+ remove_close_paren(corrector, node, buffer)
18
17
  handle_orphaned_comma(corrector, node)
19
18
 
20
19
  return unless ternary_condition?(node) && next_char_is_question_mark?(node)
@@ -24,6 +23,38 @@ module RuboCop
24
23
 
25
24
  private
26
25
 
26
+ # When the line above `)` ends with a comment and a chained call follows `)`,
27
+ # crossing the newline would pull the chain into the comment. Preserve the newline.
28
+ def remove_close_paren(corrector, node, buffer)
29
+ newlines = !comment_above_close_paren_swallows_chain?(node, buffer)
30
+ corrector.remove(range_with_surrounding_space(range: node.loc.end, buffer: buffer,
31
+ side: :left, newlines: newlines))
32
+ end
33
+
34
+ def comment_above_close_paren_swallows_chain?(node, buffer)
35
+ last_child = node.children.last
36
+ return false unless last_child
37
+
38
+ body_end = last_child.source_range.end_pos
39
+ close_paren_begin = node.loc.end.begin_pos
40
+ return false if body_end >= close_paren_begin
41
+
42
+ source_between = buffer.source[body_end...close_paren_begin]
43
+ return false unless source_between.match?(/#[^\n]*\n/)
44
+
45
+ chained_after_close_paren?(node)
46
+ end
47
+
48
+ def chained_after_close_paren?(node)
49
+ close_paren = node.loc.end
50
+ line_text = close_paren.source_line
51
+ after_paren = line_text[(close_paren.column + 1)..]
52
+ return false if after_paren.nil?
53
+
54
+ trimmed = after_paren.lstrip
55
+ !trimmed.empty? && !trimmed.start_with?('#')
56
+ end
57
+
27
58
  def ternary_condition?(node)
28
59
  node.parent&.if_type? && node.parent.ternary?
29
60
  end
@@ -3,12 +3,12 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Gemspec
6
- # An attribute assignment method calls should be listed only once
6
+ # An attribute assignment method call should be listed only once
7
7
  # in a gemspec.
8
8
  #
9
9
  # Assigning to an attribute with the same name using `spec.foo =` or
10
10
  # `spec.attribute#[]=` will be an unintended usage. On the other hand,
11
- # duplication of methods such # as `spec.requirements`,
11
+ # duplication of methods such as `spec.requirements`,
12
12
  # `spec.add_runtime_dependency`, and others are permitted because it is
13
13
  # the intended use of appending values.
14
14
  #
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Requires a gemspec to have `rubygems_mfa_required` metadata set.
7
7
  #
8
8
  # This setting tells RubyGems that MFA (Multi-Factor Authentication) is
9
- # required for accounts to be able perform privileged operations, such as
9
+ # required for accounts to be able to perform privileged operations, such as
10
10
  # (see RubyGems' documentation for the full list of privileged
11
11
  # operations):
12
12
  #
@@ -4,10 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Gemspec
6
6
  # Checks that `RUBY_VERSION` and `Ruby::VERSION` constants are not used in gemspec.
7
- # Using `RUBY_VERSION` and `Ruby::VERSION` are dangerous because value of the
7
+ # Using `RUBY_VERSION` and `Ruby::VERSION` is dangerous because the value of the
8
8
  # constant is determined by `rake release`.
9
- # It's possible to have dependency based on ruby version used
10
- # to execute `rake release` and not user's ruby version.
9
+ # It's possible to have a dependency based on the Ruby version used
10
+ # to execute `rake release` and not the user's Ruby version.
11
11
  #
12
12
  # @example
13
13
  #
@@ -46,8 +46,7 @@ module RuboCop
46
46
 
47
47
  def on_block(node)
48
48
  return unless let_rubocop_config_new?(node)
49
-
50
- describe = find_describe_method_node(node)
49
+ return unless (describe = find_describe_method_node(node))
51
50
 
52
51
  unless (exist_config = describe.last_argument.source == ':config')
53
52
  additional_message = ' and specify `:config` in `describe`'
@@ -65,7 +64,10 @@ module RuboCop
65
64
  private
66
65
 
67
66
  def find_describe_method_node(block_node)
68
- block_node.ancestors.find { |node| node.block_type? && node.method?(:describe) }.send_node
67
+ describe = block_node.ancestors.find do |ancestor|
68
+ ancestor.block_type? && ancestor.method?(:describe)
69
+ end
70
+ describe&.send_node
69
71
  end
70
72
  end
71
73
  end
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # `Layout/EndAlignment` cop aligns with keywords (e.g. `if`, `while`, `case`)
15
15
  # by default. On the other hand, `||= begin` that this cop targets tends to
16
16
  # align with the start of the line, it defaults to `EnforcedStyleAlignWith: start_of_line`.
17
- # These style can be configured by each cop.
17
+ # These styles can be configured by each cop.
18
18
  #
19
19
  # @example EnforcedStyleAlignWith: start_of_line (default)
20
20
  # # bad
@@ -18,6 +18,10 @@ module RuboCop
18
18
  # `either` (which is the default) : the `end` is allowed to be in either
19
19
  # location. The autocorrect will default to `start_of_line`.
20
20
  #
21
+ # When the `do` or `{` appears on a continuation line of multiline
22
+ # method arguments, the start of the line where the method is called
23
+ # is used as the alignment target instead of that continuation line.
24
+ #
21
25
  # @example EnforcedStyleAlignWith: either (default)
22
26
  # # bad
23
27
  #
@@ -116,6 +120,7 @@ module RuboCop
116
120
  end_loc = block_node.loc.end
117
121
  return unless begins_its_line?(end_loc)
118
122
 
123
+ start_node = start_for_line_node(block_node) if style == :start_of_line
119
124
  start_loc = start_node.source_range
120
125
  return unless start_loc.column != end_loc.column || style == :start_of_block
121
126
 
@@ -196,23 +201,34 @@ module RuboCop
196
201
 
197
202
  def compute_do_source_line_column(node, end_loc)
198
203
  do_loc = node.loc.begin # Actually it's either do or {.
204
+ anchor_loc = do_line_anchor_loc(node, do_loc)
199
205
 
200
206
  # We've found that "end" is not aligned with the start node (which
201
207
  # can be a block, a variable assignment, etc). But we also allow
202
208
  # the "end" to be aligned with the start of the line where the "do"
203
209
  # is, which is a style some people use in multi-line chains of
204
210
  # blocks.
205
- match = /\S.*/.match(do_loc.source_line)
211
+ match = /\S.*/.match(anchor_loc.source_line)
206
212
  indentation_of_do_line = match.begin(0)
207
- return unless end_loc.column != indentation_of_do_line || style == :start_of_line
213
+ permitted_columns = permitted_do_line_columns(do_loc, indentation_of_do_line)
214
+ return if permitted_columns.include?(end_loc.column) && style != :start_of_line
208
215
 
209
216
  {
210
217
  source: match[0],
211
- line: do_loc.line,
218
+ line: anchor_loc.line,
212
219
  column: indentation_of_do_line
213
220
  }
214
221
  end
215
222
 
223
+ # `end` aligned with an argument continuation line that holds the `do`
224
+ # was accepted before the anchor moved to the method dispatch line;
225
+ # keep accepting it so that such code does not become an offense.
226
+ def permitted_do_line_columns(do_loc, indentation_of_do_line)
227
+ columns = [indentation_of_do_line]
228
+ columns << (do_loc.source_line =~ /\S/) if style == :either
229
+ columns
230
+ end
231
+
216
232
  def loc_to_source_line_column(loc)
217
233
  {
218
234
  source: loc.source.lines.to_a.first.chomp,
@@ -239,7 +255,7 @@ module RuboCop
239
255
  def compute_start_col(ancestor_node, node)
240
256
  if style == :start_of_block
241
257
  do_loc = node.loc.begin
242
- return do_loc.source_line =~ /\S/
258
+ return do_line_anchor_loc(node, do_loc).source_line =~ /\S/
243
259
  end
244
260
  (ancestor_node || node).source_range.column
245
261
  end
@@ -253,6 +269,27 @@ module RuboCop
253
269
 
254
270
  corrector.remove(range)
255
271
  end
272
+
273
+ # When the `do` or `{` is on a continuation line of multiline method
274
+ # arguments, the indentation of that line is not a meaningful
275
+ # alignment target; anchor on the method dispatch position instead.
276
+ def do_line_anchor_loc(node, do_loc)
277
+ if do_line_begins_inside_argument?(node, do_loc)
278
+ node.send_node.selector || node.send_node.source_range
279
+ else
280
+ do_loc
281
+ end
282
+ end
283
+
284
+ def do_line_begins_inside_argument?(node, do_loc)
285
+ line_begin_pos = do_loc.begin_pos - do_loc.column
286
+ first_char_pos = line_begin_pos + (do_loc.source_line =~ /\S/)
287
+
288
+ (node.send_node.arguments + node.arguments).any? do |argument|
289
+ argument.source_range.begin_pos <= first_char_pos &&
290
+ first_char_pos < argument.source_range.end_pos
291
+ end
292
+ end
256
293
  end
257
294
  end
258
295
  end
@@ -49,7 +49,7 @@ module RuboCop
49
49
  # - private_methods
50
50
  # ----
51
51
  #
52
- # Instead of putting all literals in the expected order, is also
52
+ # Instead of putting all literals in the expected order, it is also
53
53
  # possible to group categories of macros. Visibility levels are handled
54
54
  # automatically.
55
55
  #
@@ -5,8 +5,10 @@ module RuboCop
5
5
  module Layout
6
6
  # Enforces empty line after guard clause.
7
7
  #
8
- # This cop allows `# :nocov:` directive after guard clause because
9
- # SimpleCov excludes code from the coverage report by wrapping it in `# :nocov:`:
8
+ # This cop allows a SimpleCov directive comment after guard clause because
9
+ # SimpleCov excludes code from the coverage report by wrapping it in such directives.
10
+ # Both the legacy `# :nocov:` comment and the newer `# simplecov:disable` /
11
+ # `# simplecov:enable` comments are recognized:
10
12
  #
11
13
  # [source,ruby]
12
14
  # ----
@@ -16,6 +18,13 @@ module RuboCop
16
18
  # # :nocov:
17
19
  # bar
18
20
  # end
21
+ #
22
+ # def foo
23
+ # # simplecov:disable
24
+ # return if condition
25
+ # # simplecov:enable
26
+ # bar
27
+ # end
19
28
  # ----
20
29
  #
21
30
  # Refer to SimpleCov's documentation for more details:
@@ -58,7 +67,7 @@ module RuboCop
58
67
 
59
68
  MSG = 'Add empty line after guard clause.'
60
69
  END_OF_HEREDOC_LINE = 1
61
- SIMPLE_DIRECTIVE_COMMENT_PATTERN = /\A# *:nocov:\z/.freeze
70
+ SIMPLECOV_COMMENT_PATTERN = /\A#\s*(?::nocov:|simplecov\s*:\s*(?:disable|enable)\b)/.freeze
62
71
 
63
72
  # @!method guard_clause_branch?(node)
64
73
  def_node_matcher :guard_clause_branch?, <<~PATTERN
@@ -213,10 +222,10 @@ module RuboCop
213
222
  parent.begin_type? && same_line?(node, node.right_sibling)
214
223
  end
215
224
 
216
- # SimpleCov excludes code from the coverage report by wrapping it in `# :nocov:`:
225
+ # SimpleCov excludes code from the coverage report by wrapping it in directive comments:
217
226
  # https://github.com/simplecov-ruby/simplecov#ignoringskipping-code
218
227
  def simplecov_directive_comment?(comment)
219
- SIMPLE_DIRECTIVE_COMMENT_PATTERN.match?(comment.text)
228
+ SIMPLECOV_COMMENT_PATTERN.match?(comment.text)
220
229
  end
221
230
  end
222
231
  end
@@ -19,10 +19,10 @@ module RuboCop
19
19
  #
20
20
  # This `Layout/EndAlignment` cop aligns with keywords (e.g. `if`, `while`, `case`)
21
21
  # by default. On the other hand, `Layout/BeginEndAlignment` cop aligns with
22
- # `EnforcedStyleAlignWith: start_of_line` by default due to `||= begin` tends
22
+ # `EnforcedStyleAlignWith: start_of_line` by default because `||= begin` tends
23
23
  # to align with the start of the line. `Layout/DefEndAlignment` cop also aligns with
24
24
  # `EnforcedStyleAlignWith: start_of_line` by default.
25
- # These style can be configured by each cop.
25
+ # These styles can be configured by each cop.
26
26
  #
27
27
  # @example EnforcedStyleAlignWith: keyword (default)
28
28
  # # bad
@@ -473,8 +473,12 @@ module RuboCop
473
473
  end
474
474
 
475
475
  def block_body_indentation_base(node, end_loc)
476
- if style == :relative_to_receiver && dot_on_new_line?(node)
476
+ return end_loc unless style == :relative_to_receiver
477
+
478
+ if dot_on_new_line?(node)
477
479
  node.send_node.loc.dot
480
+ elsif selector_on_new_line?(node)
481
+ node.send_node.loc.selector
478
482
  else
479
483
  end_loc
480
484
  end
@@ -487,6 +491,14 @@ module RuboCop
487
491
  receiver = send_node.receiver
488
492
  receiver && receiver.last_line < send_node.loc.dot.line
489
493
  end
494
+
495
+ def selector_on_new_line?(node)
496
+ send_node = node.send_node
497
+ return false unless send_node.loc?(:dot) && send_node.loc?(:selector)
498
+
499
+ receiver = send_node.receiver
500
+ receiver && receiver.last_line < send_node.loc.selector.line
501
+ end
490
502
  end
491
503
  end
492
504
  end
@@ -114,7 +114,9 @@ module RuboCop
114
114
 
115
115
  def other_cop_takes_precedence?(node)
116
116
  single_line_block_chain_enabled? && any_descendant?(node, :any_block) do |block_node|
117
- block_node.parent.send_type? && block_node.parent.loc.dot && block_node.single_line?
117
+ next unless (parent = block_node.parent)
118
+
119
+ parent.call_type? && parent.loc.dot && block_node.single_line?
118
120
  end
119
121
  end
120
122
 
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Layout
6
6
  # Checks for space between the name of a receiver and a left
7
- # brackets.
7
+ # bracket.
8
8
  #
9
9
  # @example
10
10
  #
@@ -28,7 +28,7 @@ module RuboCop
28
28
  MISTAKES = { '=-' => '-=', '=+' => '+=', '=*' => '*=', '=!' => '!=' }.freeze
29
29
 
30
30
  def on_asgn(node)
31
- return unless (rhs = rhs(node))
31
+ return unless (rhs = node.expression)
32
32
 
33
33
  range = range_between(node.loc.operator.end_pos - 1, rhs.source_range.begin_pos + 1)
34
34
  source = range.source
@@ -38,16 +38,6 @@ module RuboCop
38
38
  end
39
39
 
40
40
  SIMPLE_ASSIGNMENT_TYPES.each { |asgn_type| alias_method :"on_#{asgn_type}", :on_asgn }
41
-
42
- private
43
-
44
- def rhs(node)
45
- if node.casgn_type?
46
- node.children[2]
47
- else
48
- node.children[1]
49
- end
50
- end
51
41
  end
52
42
  end
53
43
  end
@@ -7,7 +7,7 @@ module RuboCop
7
7
  # when param passed without parentheses.
8
8
  #
9
9
  # This cop can customize allowed methods with `AllowedMethods`.
10
- # By default, there are no methods to allowed.
10
+ # By default, there are no allowed methods.
11
11
  #
12
12
  # @example
13
13
  #
@@ -44,13 +44,6 @@ module RuboCop
44
44
  RESTRICT_ON_SEND = PRECEDENCE.flatten.freeze
45
45
  MSG = 'Wrap expressions with varying precedence with parentheses to avoid ambiguity.'
46
46
 
47
- def on_new_investigation
48
- # Cache the precedence of each node being investigated
49
- # so that we only need to calculate it once
50
- @node_precedences = {}
51
- super
52
- end
53
-
54
47
  def on_and(node)
55
48
  return unless (parent = node.parent)
56
49
 
@@ -77,9 +70,7 @@ module RuboCop
77
70
  private
78
71
 
79
72
  def precedence(node)
80
- @node_precedences.fetch(node) do
81
- PRECEDENCE.index { |operators| operators.include?(operator_name(node)) }
82
- end
73
+ PRECEDENCE.index { |operators| operators.include?(operator_name(node)) }
83
74
  end
84
75
 
85
76
  def operator?(node)
@@ -80,7 +80,6 @@ module RuboCop
80
80
  check_assignment_chain(arg_name, arg_value)
81
81
  end
82
82
 
83
- # rubocop:disable Metrics/AbcSize
84
83
  def check_assignment_chain(arg_name, node)
85
84
  return unless node.lvasgn_type?
86
85
 
@@ -88,7 +87,7 @@ module RuboCop
88
87
  current_node = node
89
88
 
90
89
  while current_node.lvasgn_type?
91
- seen_variables << current_node.children.first if current_node.lvasgn_type?
90
+ seen_variables << current_node.children.first
92
91
  current_node = current_node.children.last
93
92
  end
94
93
 
@@ -99,7 +98,6 @@ module RuboCop
99
98
 
100
99
  add_offense(current_node, message: format(MSG, arg_name: arg_name))
101
100
  end
102
- # rubocop:enable Metrics/AbcSize
103
101
  end
104
102
  end
105
103
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for overwriting an exception with an exception result by use ``rescue =>``.
6
+ # Checks for overwriting an exception with an exception result by using ``rescue =>``.
7
7
  #
8
8
  # You intended to write as `rescue StandardError`.
9
9
  # However, you have written `rescue => StandardError`.
@@ -12,8 +12,13 @@ module RuboCop
12
12
  # class/module keyword definitions. It detects reassignment when a constant
13
13
  # is first defined one way and then redefined using the `NAME = value` syntax.
14
14
  #
15
- # The cop cannot catch all offenses, like, for example, when a constant
16
- # is reassigned in another file, or when using metaprogramming (`Module#const_set`).
15
+ # The cop cannot catch all offenses, like, for example, when using metaprogramming
16
+ # (`Module#const_set`).
17
+ #
18
+ # By default the cop also cannot detect reassignment across files.
19
+ # When `AllCops/UseProjectIndex` is enabled and the `rubydex` gem is installed,
20
+ # the cop additionally consults the project-wide index and reports reassignments
21
+ # whose previous definition lives in another file.
17
22
  #
18
23
  # The cop only takes into account constants assigned in a "simple" way: directly
19
24
  # inside class/module definition, or within another constant. Other type of assignments
@@ -74,7 +79,10 @@ module RuboCop
74
79
  # end
75
80
  #
76
81
  class ConstantReassignment < Base
82
+ include ProjectIndexHelp
83
+
77
84
  MSG = 'Constant `%<constant>s` is already assigned in this namespace.'
85
+ CROSS_FILE_MSG = 'Constant `%<constant>s` is already assigned in `%<path>s`.'
78
86
 
79
87
  RESTRICT_ON_SEND = %i[remove_const].freeze
80
88
 
@@ -101,9 +109,14 @@ module RuboCop
101
109
  return unless simple_assignment?(node)
102
110
 
103
111
  name = fully_qualified_constant_name(node)
104
- return constant_definitions[name] = :casgn unless constant_definitions.key?(name)
105
112
 
106
- add_offense(node, message: format(MSG, constant: constant_display_name(node)))
113
+ if constant_definitions.key?(name)
114
+ add_offense(node, message: format(MSG, constant: constant_display_name(node)))
115
+ return
116
+ end
117
+
118
+ constant_definitions[name] = :casgn
119
+ report_cross_file_collision(node, name, constant_display_name(node))
107
120
  end
108
121
 
109
122
  def on_send(node)
@@ -192,6 +205,25 @@ module RuboCop
192
205
  def constant_definitions
193
206
  @constant_definitions ||= {}
194
207
  end
208
+
209
+ def report_cross_file_collision(node, fully_qualified_name, display_name)
210
+ return unless project_index
211
+ return unless (declaration = project_index[fully_qualified_name.delete_prefix('::')])
212
+ return unless (prior = prior_definition_in_other_file(declaration))
213
+
214
+ msg = format(CROSS_FILE_MSG, constant: display_name, path: prior.location.to_file_path)
215
+
216
+ add_offense(node, message: msg)
217
+ end
218
+
219
+ def prior_definition_in_other_file(declaration)
220
+ current = processed_source.file_path
221
+
222
+ declaration.definitions.find do |definition|
223
+ other = definition.location.to_file_path
224
+ !File.identical?(other, current)
225
+ end
226
+ end
195
227
  end
196
228
  end
197
229
  end