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
@@ -13,12 +13,12 @@ module RuboCop
13
13
  #
14
14
  # Large projects will over time end up with one or two constant names that
15
15
  # are problematic because of a conflict with a library or just internally
16
- # using the same name a namespace and a class. To avoid too many unnecessary
17
- # offenses, Enable this cop with `Only: [The, Constant, Names, Causing, Issues]`
16
+ # using the same name for a namespace and a class. To avoid too many unnecessary
17
+ # offenses, enable this cop with `Only: [The, Constant, Names, Causing, Issues]`
18
18
  #
19
- # NOTE: `Style/RedundantConstantBase` cop is disabled if this cop is enabled to prevent
20
- # conflicting rules. Because it respects user configurations that want to enable
21
- # this cop which is disabled by default.
19
+ # NOTE: `Style/RedundantConstantBase` cop is disabled if this cop is enabled,
20
+ # to prevent conflicting rules. This is because it respects user configurations
21
+ # that want to enable this cop which is disabled by default.
22
22
  #
23
23
  # @example
24
24
  # # By default checks every constant
@@ -73,7 +73,6 @@ module RuboCop
73
73
  # require 'my_debugger/start'
74
74
  class Debugger < Base
75
75
  MSG = 'Remove debugger entry point `%<source>s`.'
76
- BLOCK_TYPES = %i[block numblock itblock kwbegin].freeze
77
76
 
78
77
  def on_send(node)
79
78
  return if assumed_usage_context?(node)
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # Alternative: 'alternative_value'
15
15
  # DeprecatedVersion: 'deprecated_version'
16
16
  #
17
- # By default, `NIL`, `TRUE`, `FALSE`, `Net::HTTPServerException, `Random::DEFAULT`,
17
+ # By default, `NIL`, `TRUE`, `FALSE`, `Net::HTTPServerException`, `Random::DEFAULT`,
18
18
  # `Struct::Group`, and `Struct::Passwd` are configured.
19
19
  #
20
20
  # @example
@@ -49,7 +49,7 @@ module RuboCop
49
49
  # Maybe further investigation of RuboCop AST will lead to an essential solution.
50
50
  return unless node.loc
51
51
 
52
- constant = node.absolute? ? constant_name(node, node.short_name) : node.source
52
+ constant = node.source.delete_prefix('::')
53
53
  return unless (deprecated_constant = deprecated_constants[constant])
54
54
 
55
55
  alternative = deprecated_constant['Alternative']
@@ -63,12 +63,6 @@ module RuboCop
63
63
 
64
64
  private
65
65
 
66
- def constant_name(node, nested_constant_name)
67
- return nested_constant_name.to_s unless node.namespace.const_type?
68
-
69
- constant_name(node.namespace, "#{node.namespace.short_name}::#{nested_constant_name}")
70
- end
71
-
72
66
  def message(good, bad, deprecated_version)
73
67
  deprecated_message = ", deprecated since Ruby #{deprecated_version}" if deprecated_version
74
68
 
@@ -77,7 +77,7 @@ module RuboCop
77
77
  return false unless processed_source.contains_comment?(node.source_range)
78
78
 
79
79
  line_comment = processed_source.comment_at_line(node.source_range.line)
80
- !line_comment || !comment_disables_cop?(line_comment.source)
80
+ !line_comment || !comment_disables_cop?(line_comment)
81
81
  end
82
82
 
83
83
  def allow_empty_lambdas?
@@ -85,8 +85,8 @@ module RuboCop
85
85
  end
86
86
 
87
87
  def comment_disables_cop?(comment)
88
- regexp_pattern = "# rubocop : (disable|todo) ([^,],)* (all|#{cop_name})"
89
- Regexp.new(regexp_pattern.gsub(' ', '\s*')).match?(comment)
88
+ directive = DirectiveComment.new(comment)
89
+ directive.disabled? && directive.cop_names.include?(cop_name)
90
90
  end
91
91
  end
92
92
  end
@@ -43,7 +43,25 @@ module RuboCop
43
43
  MSG = 'Do not return from an `ensure` block.'
44
44
 
45
45
  def on_ensure(node)
46
- node.branch&.each_node(:return) { |return_node| add_offense(return_node) }
46
+ node.branch&.each_node(:return) do |return_node|
47
+ next if return_from_inner_scope?(return_node, node)
48
+
49
+ add_offense(return_node)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # A `return` inside a nested method definition or lambda within the
56
+ # `ensure` returns from that inner scope, not from the method whose
57
+ # `ensure` this is, so it is not an offense. A `return` inside a plain
58
+ # block (or `proc`) does propagate out, so it remains an offense.
59
+ def return_from_inner_scope?(return_node, ensure_node)
60
+ return_node.each_ancestor do |ancestor|
61
+ break if ancestor == ensure_node
62
+ return true if ancestor.any_def_type? || (ancestor.any_block_type? && ancestor.lambda?)
63
+ end
64
+ false
47
65
  end
48
66
  end
49
67
  end
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # `ERB.new` with non-keyword arguments is deprecated since ERB 2.2.0.
24
24
  # Use `:trim_mode` and `:eoutvar` keyword arguments to `ERB.new`.
25
25
  # This cop identifies places where `ERB.new(str, trim_mode, eoutvar)` can
26
- # be replaced by `ERB.new(str, :trim_mode: trim_mode, eoutvar: eoutvar)`.
26
+ # be replaced by `ERB.new(str, trim_mode: trim_mode, eoutvar: eoutvar)`.
27
27
  #
28
28
  # @example
29
29
  # # Target codes supports Ruby 2.6 and higher only
@@ -148,7 +148,9 @@ module RuboCop
148
148
  arguments = node.arguments
149
149
  overridden_kwargs = kwargs.dup
150
150
 
151
- overridden_kwargs[0] = "trim_mode: #{arguments[2].source}" if arguments[2]
151
+ if arguments[2] && !arguments[2].hash_type?
152
+ overridden_kwargs[0] = "trim_mode: #{arguments[2].source}"
153
+ end
152
154
 
153
155
  if arguments[3] && !arguments[3].hash_type?
154
156
  overridden_kwargs[1] = "eoutvar: #{arguments[3].source}"
@@ -104,6 +104,7 @@ module RuboCop
104
104
 
105
105
  def literal_safe?(node)
106
106
  return false unless node
107
+ return literal_safe?(node.children.first) if node.begin_type?
107
108
 
108
109
  (node.numeric_type? && node.value.zero?) || node.nil_type?
109
110
  end
@@ -61,11 +61,15 @@ module RuboCop
61
61
  private
62
62
 
63
63
  def scheduler_compatible?(io1, io2)
64
- return false unless io1&.array_type? && io1.values.size == 1
64
+ return false unless single_io_array?(io1)
65
65
 
66
66
  io2&.array_type? ? io2.values.empty? : (io2.nil? || io2.nil_type?)
67
67
  end
68
68
 
69
+ def single_io_array?(node)
70
+ node&.array_type? && node.values.size == 1 && !node.values.first.splat_type?
71
+ end
72
+
69
73
  def preferred_method(read, write, timeout)
70
74
  timeout_argument = timeout.nil? ? '' : "(#{timeout.source})"
71
75
 
@@ -24,8 +24,25 @@ module RuboCop
24
24
  MSG = 'Interpolation in single quoted string detected. ' \
25
25
  'Use double quoted strings if you need interpolation.'
26
26
 
27
- # rubocop:disable Metrics/CyclomaticComplexity
28
27
  def on_str(node)
28
+ check(node)
29
+ end
30
+
31
+ # A multiline single-quoted string is parsed as a `dstr` of `str` segments, so it
32
+ # is not covered by `on_str`. Inspect single-quoted `dstr`s here; double-quoted
33
+ # interpolation is also a `dstr`, hence the delimiter check.
34
+ def on_dstr(node)
35
+ # A heredoc is also a `dstr`, but its `loc` is a `Parser::Source::Map::Heredoc`
36
+ # with no `begin`, so bail before touching it.
37
+ return if heredoc?(node)
38
+
39
+ check(node) if node.loc.begin&.source == "'"
40
+ end
41
+
42
+ private
43
+
44
+ # rubocop:disable Metrics/CyclomaticComplexity
45
+ def check(node)
29
46
  return if node.parent&.regexp_type?
30
47
  return unless /(?<!\\)#\{.*\}/.match?(node.source)
31
48
  return if heredoc?(node)
@@ -36,8 +53,6 @@ module RuboCop
36
53
  end
37
54
  # rubocop:enable Metrics/CyclomaticComplexity
38
55
 
39
- private
40
-
41
56
  def autocorrect(corrector, node)
42
57
  starting_token, ending_token = if node.source.include?('"')
43
58
  ['%{', '}']
@@ -42,7 +42,7 @@ module RuboCop
42
42
  end
43
43
 
44
44
  add_offense(node) do |corrector|
45
- corrector.replace(node, node.first_argument.source.delete('&'))
45
+ corrector.replace(node, node.first_argument.source.delete_prefix('&'))
46
46
  end
47
47
  end
48
48
  end
@@ -56,7 +56,17 @@ module RuboCop
56
56
  def traverse_node(node, &block)
57
57
  yield node if node.equals_asgn?
58
58
 
59
- node.each_child_node { |child| traverse_node(child, &block) }
59
+ node.each_child_node do |child|
60
+ next if scope_body?(node, child)
61
+
62
+ traverse_node(child, &block)
63
+ end
64
+ end
65
+
66
+ # An assignment inside a block or method body within the condition belongs to
67
+ # that inner scope rather than the condition itself, so it is not inspected.
68
+ def scope_body?(node, child)
69
+ node.type?(:any_block, :any_def) && child == node.body
60
70
  end
61
71
 
62
72
  def all_literals?(node)
@@ -119,11 +119,13 @@ module RuboCop
119
119
  end
120
120
 
121
121
  def autocorrected_value_for_string(node)
122
- if node.source.start_with?("'", '%q')
123
- node.children.last.inspect[1..-2]
124
- else
125
- node.children.last
126
- end
122
+ return node.source.delete_prefix('"').delete_suffix('"') unless node.value.valid_encoding?
123
+
124
+ escape_string_content(node.children.last)
125
+ end
126
+
127
+ def escape_string_content(string)
128
+ string.gsub(/[\\"]|#(?=[@{$])/, '\\\\\&')
127
129
  end
128
130
 
129
131
  def autocorrected_value_for_symbol(node)
@@ -134,12 +136,7 @@ module RuboCop
134
136
  end
135
137
 
136
138
  def autocorrected_value_in_hash_for_symbol(node)
137
- # TODO: We need to detect symbol unacceptable names more correctly
138
- if / |"|'/.match?(node.value.to_s)
139
- ":\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\""
140
- else
141
- ":#{node.value}"
142
- end
139
+ escape_string_content(node.value.inspect)
143
140
  end
144
141
 
145
142
  def autocorrected_value_for_array(node)
@@ -6,10 +6,10 @@ module RuboCop
6
6
  module Lint
7
7
  # Checks that there is an `# rubocop:enable ...` statement
8
8
  # after a `# rubocop:disable ...` statement. This will prevent leaving
9
- # cop disables on wide ranges of code, that latter contributors to
9
+ # cop disables on wide ranges of code, that later contributors to
10
10
  # a file wouldn't be aware of.
11
11
  #
12
- # You can set `MaximumRangeSize` to define the maximum number of
12
+ # You can set `MaxRangeSize` to define the maximum number of
13
13
  # consecutive lines a cop can be disabled for.
14
14
  #
15
15
  # - `.inf` any size (default)
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # # rubocop:enable SomeCop
24
24
  # ----
25
25
  #
26
- # @example MaximumRangeSize: .inf (default)
26
+ # @example MaxRangeSize: .inf (default)
27
27
  #
28
28
  # # good
29
29
  # # rubocop:disable Layout/SpaceAroundOperators
@@ -37,7 +37,7 @@ module RuboCop
37
37
  # x= 0
38
38
  # # EOF
39
39
  #
40
- # @example MaximumRangeSize: 2
40
+ # @example MaxRangeSize: 2
41
41
  #
42
42
  # # good
43
43
  # # rubocop:disable Layout/SpaceAroundOperators
@@ -94,7 +94,7 @@ module RuboCop
94
94
  end
95
95
 
96
96
  def max_range
97
- @max_range ||= cop_config['MaximumRangeSize']
97
+ @max_range ||= cop_config['MaxRangeSize']
98
98
  end
99
99
 
100
100
  def message(cop, comment, type = 'cop')
@@ -4,8 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # In math and Python, we can use `x < y < z` style comparison to compare
7
- # multiple value. However, we can't use the comparison in Ruby. However,
8
- # the comparison is not syntax error. This cop checks the bad usage of
7
+ # multiple values. However, we can't use the comparison in Ruby. However,
8
+ # the comparison is not a syntax error. This cop checks the bad usage of
9
9
  # comparison operators.
10
10
  #
11
11
  # @example
@@ -41,6 +41,8 @@ module RuboCop
41
41
  def on_lvasgn(node)
42
42
  node.each_node(:kwbegin) do |kwbegin_node|
43
43
  kwbegin_node.each_node(:return) do |return_node|
44
+ next if return_from_inner_scope?(return_node, kwbegin_node)
45
+
44
46
  add_offense(return_node)
45
47
  end
46
48
  end
@@ -51,6 +53,20 @@ module RuboCop
51
53
  alias on_casgn on_lvasgn
52
54
  alias on_or_asgn on_lvasgn
53
55
  alias on_op_asgn on_lvasgn
56
+
57
+ private
58
+
59
+ # A `return` inside a nested method definition or lambda within the
60
+ # `begin..end` returns from that inner scope rather than the assignment
61
+ # context, so it is not an offense. A `return` inside a plain block (or
62
+ # `proc`) does propagate out, so it remains an offense.
63
+ def return_from_inner_scope?(return_node, kwbegin_node)
64
+ return_node.each_ancestor do |ancestor|
65
+ break if ancestor == kwbegin_node
66
+ return true if ancestor.any_def_type? || (ancestor.any_block_type? && ancestor.lambda?)
67
+ end
68
+ false
69
+ end
54
70
  end
55
71
  end
56
72
  end
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # always sort the list.
13
13
  #
14
14
  # `Dir.glob` and `Dir[]` sort globbed results by default in Ruby 3.0.
15
- # So all bad cases are acceptable when Ruby 3.0 or higher are used.
15
+ # So all bad cases are acceptable when Ruby 3.0 or higher is used.
16
16
  #
17
17
  # NOTE: This cop will be deprecated and removed when supporting only Ruby 3.0 and higher.
18
18
  #
@@ -74,7 +74,7 @@ module RuboCop
74
74
  end
75
75
 
76
76
  # @!method chained_send?(node)
77
- def_node_matcher :chained_send?, '(send !nil? ...)'
77
+ def_node_matcher :chained_send?, '(call !nil? ...)'
78
78
 
79
79
  # @!method define_method?(node)
80
80
  def_node_matcher :define_method?, <<~PATTERN
@@ -3,9 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Warns the usage of unsafe number conversions. Unsafe
7
- # number conversion can cause unexpected error if auto type conversion
8
- # fails. Cop prefer parsing with number class instead.
6
+ # Warns against the usage of unsafe number conversions. Unsafe
7
+ # number conversion can cause an unexpected error if auto type conversion
8
+ # fails. The cop prefers parsing with a number class instead.
9
9
  #
10
10
  # Conversion with `Integer`, `Float`, etc. will raise an `ArgumentError`
11
11
  # if given input that is not numeric (eg. an empty string), whereas
@@ -14,10 +14,10 @@ module RuboCop
14
14
  # always correct to raise if a value is not numeric.
15
15
  #
16
16
  # NOTE: Some values cannot be converted properly using one of the `Kernel`
17
- # method (for instance, `Time` and `DateTime` values are allowed by this
17
+ # methods (for instance, `Time` and `DateTime` values are allowed by this
18
18
  # cop by default). Similarly, Rails' duration methods do not work well
19
19
  # with `Integer()` and can be allowed with `AllowedMethods`. By default,
20
- # there are no methods to allowed.
20
+ # there are no allowed methods.
21
21
  #
22
22
  # @safety
23
23
  # Autocorrection is unsafe because it is not guaranteed that the
@@ -66,7 +66,7 @@ module RuboCop
66
66
  # # good
67
67
  # 10.minutes.to_i
68
68
  #
69
- # @example IgnoredClasses: [Time, DateTime] (default)
69
+ # @example AllowedClasses: [Time, DateTime] (default)
70
70
  #
71
71
  # # good
72
72
  # Time.now.to_datetime.to_i
@@ -117,11 +117,11 @@ module RuboCop
117
117
 
118
118
  message = format(
119
119
  MSG,
120
- current: "#{receiver.source}.#{to_method}",
120
+ current: current_method(node, receiver, to_method),
121
121
  corrected_method: correct_method(node, receiver)
122
122
  )
123
123
  add_offense(node, message: message) do |corrector|
124
- next if part_of_ignored_node?(node)
124
+ next if safe_navigation?(node) || part_of_ignored_node?(node)
125
125
 
126
126
  corrector.replace(node, correct_method(node, node.receiver))
127
127
 
@@ -156,11 +156,20 @@ module RuboCop
156
156
  "{ |i| #{body} }"
157
157
  end
158
158
 
159
+ def current_method(node, receiver, to_method)
160
+ operator = node.csend_type? ? '&.' : '.'
161
+ "#{receiver.source}#{operator}#{to_method}"
162
+ end
163
+
159
164
  def remove_parentheses(corrector, node)
160
165
  corrector.replace(node.loc.begin, ' ')
161
166
  corrector.remove(node.loc.end)
162
167
  end
163
168
 
169
+ def safe_navigation?(node)
170
+ node.csend_type? || node.each_descendant(:csend).any?
171
+ end
172
+
164
173
  def allow_receiver?(receiver)
165
174
  if receiver.numeric_type? || (receiver.call_type? &&
166
175
  (conversion_method?(receiver.method_name) ||
@@ -188,7 +197,7 @@ module RuboCop
188
197
  end
189
198
 
190
199
  def ignored_classes
191
- cop_config.fetch('IgnoredClasses', [])
200
+ cop_config.fetch('AllowedClasses', [])
192
201
  end
193
202
 
194
203
  def ignored_class?(name)
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
17
17
  # -e:1: _1 is reserved for numbered parameter
18
18
  #
19
- # NOTE: The parametered parameters are from `_1` to `_9`. This cop checks `_0`, and over `_10`
19
+ # NOTE: The numbered parameters are from `_1` to `_9`. This cop checks `_0`, and over `_10`
20
20
  # as well to prevent confusion.
21
21
  #
22
22
  # @example
@@ -58,6 +58,9 @@ module RuboCop
58
58
  '(op-asgn (lvasgn $_lhs) $_operation ({int lvar} $_rhs))'
59
59
 
60
60
  def on_send(node)
61
+ # Safe navigation short-circuits to `nil` when the receiver is `nil`, so the
62
+ # result is not constant and replacing it with `0`/`1` would change behavior.
63
+ return if node.csend_type?
61
64
  return unless (lhs, operation, rhs = operation_with_constant_result?(node))
62
65
  return unless (result = constant_result?(lhs, operation, rhs))
63
66
 
@@ -38,23 +38,23 @@ module RuboCop
38
38
  def on_new_investigation
39
39
  return if processed_source.buffer.source.empty?
40
40
 
41
- encoding_line, frozen_string_literal_line = magic_comment_lines
41
+ encoding_line, other_magic_comment_line = magic_comment_lines
42
42
 
43
- return unless encoding_line && frozen_string_literal_line
44
- return if encoding_line < frozen_string_literal_line
43
+ return unless encoding_line && other_magic_comment_line
44
+ return if encoding_line < other_magic_comment_line
45
45
 
46
46
  range = processed_source.buffer.line_range(encoding_line + 1)
47
47
 
48
48
  add_offense(range) do |corrector|
49
- autocorrect(corrector, encoding_line, frozen_string_literal_line)
49
+ autocorrect(corrector, encoding_line, other_magic_comment_line)
50
50
  end
51
51
  end
52
52
 
53
53
  private
54
54
 
55
- def autocorrect(corrector, encoding_line, frozen_string_literal_line)
55
+ def autocorrect(corrector, encoding_line, other_magic_comment_line)
56
56
  range1 = processed_source.buffer.line_range(encoding_line + 1)
57
- range2 = processed_source.buffer.line_range(frozen_string_literal_line + 1)
57
+ range2 = processed_source.buffer.line_range(other_magic_comment_line + 1)
58
58
 
59
59
  corrector.replace(range1, range2.source)
60
60
  corrector.replace(range2, range1.source)
@@ -66,7 +66,7 @@ module RuboCop
66
66
  leading_magic_comments.each.with_index do |comment, index|
67
67
  if comment.encoding_specified?
68
68
  lines[0] = index
69
- elsif comment.frozen_string_literal_specified?
69
+ elsif comment.valid?
70
70
  lines[1] = index
71
71
  end
72
72
 
@@ -63,7 +63,7 @@ module RuboCop
63
63
  end
64
64
 
65
65
  def spaces_before_left_parenthesis(node)
66
- return 0 if node.parenthesized?
66
+ return 0 if node.parenthesized? || !node.first_argument.source.start_with?('(')
67
67
 
68
68
  node.first_argument.source_range.begin_pos - node.loc.selector.end_pos
69
69
  end
@@ -95,7 +95,7 @@ module RuboCop
95
95
  if parent.module_type?
96
96
  namespace = parent.identifier.source
97
97
 
98
- return allow_implicit_namespaces.include?(namespace)
98
+ return true if allow_implicit_namespaces.include?(namespace)
99
99
  end
100
100
 
101
101
  implicit_namespace?(parent)
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for `rand(1)` calls.
7
- # Such calls always return `0`.
7
+ # Such calls always return `0`, and so do `rand(-1)`, `rand(1.0)`, and `rand(-1.0)`.
8
8
  #
9
9
  # @example
10
10
  #
@@ -283,7 +283,10 @@ module RuboCop
283
283
  end
284
284
 
285
285
  def matching_range(haystack, needle)
286
- offset = haystack.source.index(needle)
286
+ # Match the cop name as a whole token so a shorter name is not found inside a
287
+ # longer one that shares its prefix (e.g. `Lint/AmbiguousOperator` in
288
+ # `Lint/AmbiguousOperatorPrecedence`).
289
+ offset = haystack.source.index(/#{Regexp.escape(needle)}(?!\w)/)
287
290
  return unless offset
288
291
 
289
292
  offset += haystack.begin_pos
@@ -6,8 +6,8 @@ module RuboCop
6
6
  # Detects instances of rubocop:enable comments that can be
7
7
  # removed.
8
8
  #
9
- # When comment enables all cops at once `rubocop:enable all`
10
- # that cop checks whether any cop was actually enabled.
9
+ # When a comment enables all cops at once `rubocop:enable all`
10
+ # the cop checks whether any cop was actually enabled.
11
11
  #
12
12
  # @example
13
13
  #
@@ -74,7 +74,10 @@ module RuboCop
74
74
  end
75
75
 
76
76
  def cop_name_indention(comment, name)
77
- comment.text.index(name)
77
+ # Match the cop name as a whole token so a shorter name is not found inside a
78
+ # longer one that shares its prefix (e.g. `Layout/EmptyLines` in
79
+ # `Layout/EmptyLinesAfterModuleInclusion`).
80
+ comment.text.index(/#{Regexp.escape(name)}(?!\w)/)
78
81
  end
79
82
 
80
83
  def range_with_comma(comment, name)
@@ -38,10 +38,10 @@ module RuboCop
38
38
  GLOB_METHODS = %i[glob []].freeze
39
39
 
40
40
  def on_send(node)
41
- return unless (receiver = node.receiver)
42
- return unless receiver.receiver&.const_type? && receiver.receiver.short_name == :Dir
43
- return unless GLOB_METHODS.include?(receiver.method_name)
44
- return if multiple_argument?(receiver)
41
+ return unless dir_glob?(node.receiver)
42
+ # `sort` with a comparator block or block-pass changes the order, so it is
43
+ # not redundant with the default sorting performed by `Dir.glob`/`Dir[]`.
44
+ return if sort_with_comparator?(node) || multiple_argument?(node.receiver)
45
45
 
46
46
  selector = node.loc.selector
47
47
 
@@ -53,9 +53,20 @@ module RuboCop
53
53
 
54
54
  private
55
55
 
56
+ def dir_glob?(receiver)
57
+ return false unless receiver&.receiver&.const_type?
58
+ return false unless receiver.receiver.short_name == :Dir
59
+
60
+ GLOB_METHODS.include?(receiver.method_name)
61
+ end
62
+
56
63
  def multiple_argument?(glob_method)
57
64
  glob_method.arguments.count >= 2 || glob_method.first_argument&.splat_type?
58
65
  end
66
+
67
+ def sort_with_comparator?(node)
68
+ node.parent&.any_block_type? || node.last_argument&.block_pass_type?
69
+ end
59
70
  end
60
71
  end
61
72
  end
@@ -183,7 +183,7 @@ module RuboCop
183
183
  def_node_matcher :conversion_with_default?, <<~PATTERN
184
184
  {
185
185
  (or $(csend _ :to_h) (hash))
186
- (or (block $(csend _ :to_h) ...) (hash))
186
+ (or (any_block $(csend _ :to_h) ...) (hash))
187
187
  (or $(csend _ :to_a) (array))
188
188
  (or $(csend _ :to_i) (int 0))
189
189
  (or $(csend _ :to_f) (float 0.0))
@@ -191,7 +191,6 @@ module RuboCop
191
191
  }
192
192
  PATTERN
193
193
 
194
- # rubocop:disable Metrics/AbcSize
195
194
  def on_csend(node)
196
195
  range = node.loc.dot
197
196
 
@@ -204,14 +203,10 @@ module RuboCop
204
203
  end
205
204
  end
206
205
 
207
- unless assume_receiver_instance_exists?(node.receiver)
208
- return if !guaranteed_instance?(node.receiver) && !check?(node)
209
- return if respond_to_nil_method?(node)
210
- end
206
+ return if guarded_by_nil_receiver?(node)
211
207
 
212
208
  add_offense(range) { |corrector| corrector.replace(range, '.') }
213
209
  end
214
- # rubocop:enable Metrics/AbcSize
215
210
 
216
211
  # rubocop:disable Metrics/AbcSize
217
212
  def on_or(node)
@@ -230,6 +225,18 @@ module RuboCop
230
225
 
231
226
  private
232
227
 
228
+ # Returns true when the `&.` is meaningful because the receiver may actually be nil.
229
+ def guarded_by_nil_receiver?(node)
230
+ return false if assume_receiver_instance_exists?(node.receiver)
231
+
232
+ guaranteed_instance = guaranteed_instance?(node.receiver)
233
+ return true if !guaranteed_instance && !check?(node)
234
+
235
+ # `nil.respond_to?(<nil method>)` is `true`, so `&.` is meaningful when the receiver
236
+ # may be nil. A guaranteed instance can never be nil, so `&.` is still redundant there.
237
+ respond_to_nil_method?(node) && !guaranteed_instance
238
+ end
239
+
233
240
  def assume_receiver_instance_exists?(receiver)
234
241
  return true if receiver.const_type? && !receiver.short_name.match?(SNAKE_CASE)
235
242