rubocop 1.6.0 → 1.9.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +3 -2
  4. data/config/default.yml +142 -19
  5. data/lib/rubocop.rb +15 -1
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  7. data/lib/rubocop/comment_config.rb +6 -6
  8. data/lib/rubocop/config.rb +10 -7
  9. data/lib/rubocop/config_loader.rb +11 -14
  10. data/lib/rubocop/config_loader_resolver.rb +21 -4
  11. data/lib/rubocop/config_obsoletion.rb +5 -3
  12. data/lib/rubocop/config_obsoletion/extracted_cop.rb +6 -6
  13. data/lib/rubocop/config_store.rb +12 -1
  14. data/lib/rubocop/cop/base.rb +1 -1
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
  16. data/lib/rubocop/cop/generator.rb +1 -3
  17. data/lib/rubocop/cop/internal_affairs.rb +6 -1
  18. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  19. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  20. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  21. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  22. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +145 -0
  23. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  24. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +19 -3
  25. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +14 -0
  26. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  27. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
  28. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
  29. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  30. data/lib/rubocop/cop/layout/space_before_brackets.rb +62 -0
  31. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
  32. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
  33. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
  34. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
  35. data/lib/rubocop/cop/lint/deprecated_constants.rb +75 -0
  36. data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
  37. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
  38. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
  39. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  40. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  41. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  42. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
  43. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
  44. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
  45. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
  46. data/lib/rubocop/cop/lint/symbol_conversion.rb +102 -0
  47. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  48. data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
  49. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  50. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
  51. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  52. data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
  53. data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
  54. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  55. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
  56. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  57. data/lib/rubocop/cop/naming/variable_name.rb +2 -0
  58. data/lib/rubocop/cop/naming/variable_number.rb +2 -9
  59. data/lib/rubocop/cop/registry.rb +10 -0
  60. data/lib/rubocop/cop/severity.rb +3 -3
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
  62. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  63. data/lib/rubocop/cop/style/collection_methods.rb +14 -1
  64. data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
  65. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  66. data/lib/rubocop/cop/style/empty_literal.rb +6 -2
  67. data/lib/rubocop/cop/style/endless_method.rb +102 -0
  68. data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
  69. data/lib/rubocop/cop/style/explicit_block_argument.rb +10 -0
  70. data/lib/rubocop/cop/style/float_division.rb +3 -0
  71. data/lib/rubocop/cop/style/for.rb +2 -0
  72. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  73. data/lib/rubocop/cop/style/hash_except.rb +95 -0
  74. data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
  75. data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
  76. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +96 -0
  77. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
  78. data/lib/rubocop/cop/style/lambda_call.rb +2 -1
  79. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -0
  80. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
  81. data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
  82. data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
  83. data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
  84. data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
  85. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
  86. data/lib/rubocop/cop/style/nil_comparison.rb +1 -0
  87. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  88. data/lib/rubocop/cop/style/raise_args.rb +5 -2
  89. data/lib/rubocop/cop/style/redundant_argument.rb +7 -1
  90. data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
  91. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  92. data/lib/rubocop/cop/style/single_line_methods.rb +34 -2
  93. data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
  94. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  95. data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
  96. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  97. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  98. data/lib/rubocop/cop/util.rb +3 -1
  99. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  100. data/lib/rubocop/magic_comment.rb +30 -1
  101. data/lib/rubocop/options.rb +10 -10
  102. data/lib/rubocop/rspec/cop_helper.rb +0 -4
  103. data/lib/rubocop/rspec/expect_offense.rb +37 -22
  104. data/lib/rubocop/runner.rb +17 -1
  105. data/lib/rubocop/target_finder.rb +4 -2
  106. data/lib/rubocop/target_ruby.rb +47 -11
  107. data/lib/rubocop/util.rb +16 -0
  108. data/lib/rubocop/version.rb +8 -2
  109. metadata +26 -7
@@ -111,7 +111,8 @@ module RuboCop
111
111
  braces_with_contents_inside(node, inner)
112
112
  elsif style_for_empty_braces == :no_space
113
113
  offense(range.begin_pos, range.end_pos,
114
- 'Space inside empty braces detected.')
114
+ 'Space inside empty braces detected.',
115
+ 'EnforcedStyleForEmptyBraces')
115
116
  end
116
117
  end
117
118
  end
@@ -120,7 +121,8 @@ module RuboCop
120
121
  return if style_for_empty_braces != :space
121
122
 
122
123
  offense(left_brace.begin_pos, right_brace.end_pos,
123
- 'Space missing inside empty braces.')
124
+ 'Space missing inside empty braces.',
125
+ 'EnforcedStyleForEmptyBraces')
124
126
  end
125
127
 
126
128
  def braces_with_contents_inside(node, inner)
@@ -164,9 +166,9 @@ module RuboCop
164
166
  if left_brace.end_pos == args_delimiter.begin_pos &&
165
167
  cop_config['SpaceBeforeBlockParameters']
166
168
  offense(left_brace.begin_pos, args_delimiter.end_pos,
167
- 'Space between { and | missing.') do
168
- opposite_style_detected
169
- end
169
+ 'Space between { and | missing.')
170
+ else
171
+ correct_style_detected
170
172
  end
171
173
  else
172
174
  # We indicate the position after the left brace. Otherwise it's
@@ -179,11 +181,11 @@ module RuboCop
179
181
 
180
182
  def space_inside_left_brace(left_brace, args_delimiter)
181
183
  if pipe?(args_delimiter)
182
- unless cop_config['SpaceBeforeBlockParameters']
184
+ if cop_config['SpaceBeforeBlockParameters']
185
+ correct_style_detected
186
+ else
183
187
  offense(left_brace.end_pos, args_delimiter.begin_pos,
184
- 'Space between { and | detected.') do
185
- opposite_style_detected
186
- end
188
+ 'Space between { and | detected.')
187
189
  end
188
190
  else
189
191
  brace_with_space = range_with_surrounding_space(range: left_brace,
@@ -220,7 +222,7 @@ module RuboCop
220
222
  end
221
223
  end
222
224
 
223
- def offense(begin_pos, end_pos, msg)
225
+ def offense(begin_pos, end_pos, msg, style_param = 'EnforcedStyle')
224
226
  range = range_between(begin_pos, end_pos)
225
227
  add_offense(range, message: msg) do |corrector|
226
228
  case range.source
@@ -229,6 +231,7 @@ module RuboCop
229
231
  when '{|' then corrector.replace(range, '{ |')
230
232
  else corrector.insert_before(range, ' ')
231
233
  end
234
+ opposite_style_detected if style_param == 'EnforcedStyle'
232
235
  end
233
236
  end
234
237
 
@@ -112,13 +112,13 @@ module RuboCop
112
112
 
113
113
  def incorrect_style_detected(token1, token2,
114
114
  expect_space, is_empty_braces)
115
- return unless ambiguous_or_unexpected_style_detected(style, token1.text == token2.text)
116
-
117
115
  brace = (token1.text == '{' ? token1 : token2).pos
118
116
  range = expect_space ? brace : space_range(brace)
117
+ detected_style = expect_space ? 'no_space' : 'space'
119
118
 
120
119
  add_offense(range, message: message(brace, is_empty_braces, expect_space)) do |corrector|
121
120
  autocorrect(corrector, range)
121
+ ambiguous_or_unexpected_style_detected(detected_style, token1.text == token2.text)
122
122
  end
123
123
  end
124
124
 
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for mistyped shorthand assignments.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # x =- y
11
+ # x =+ y
12
+ # x =* y
13
+ # x =! y
14
+ #
15
+ # # good
16
+ # x -= y # or x = -y
17
+ # x += y # or x = +y
18
+ # x *= y # or x = *y
19
+ # x != y # or x = !y
20
+ #
21
+ class AmbiguousAssignment < Base
22
+ include RangeHelp
23
+
24
+ MSG = 'Suspicious assignment detected. Did you mean `%<op>s`?'
25
+
26
+ SIMPLE_ASSIGNMENT_TYPES = %i[lvasgn ivasgn cvasgn gvasgn casgn].freeze
27
+
28
+ MISTAKES = {
29
+ '=-' => '-=',
30
+ '=+' => '+=',
31
+ '=*' => '*=',
32
+ '=!' => '!='
33
+ }.freeze
34
+
35
+ def on_asgn(node)
36
+ return unless (rhs = rhs(node))
37
+
38
+ range = range_between(node.loc.operator.end_pos - 1, rhs.source_range.begin_pos + 1)
39
+ source = range.source
40
+ return unless MISTAKES.key?(source)
41
+
42
+ add_offense(range, message: format(MSG, op: MISTAKES[source]))
43
+ end
44
+
45
+ SIMPLE_ASSIGNMENT_TYPES.each { |asgn_type| alias_method :"on_#{asgn_type}", :on_asgn }
46
+
47
+ private
48
+
49
+ def rhs(node)
50
+ if node.casgn_type?
51
+ node.children[2]
52
+ else
53
+ node.children[1]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -17,6 +17,7 @@ module RuboCop
17
17
  #
18
18
  # @example
19
19
  # # bad
20
+ # x / x
20
21
  # x.top >= x.top
21
22
  #
22
23
  # if a.x != 0 && a.x != 0
@@ -27,15 +28,19 @@ module RuboCop
27
28
  # left_child || left_child
28
29
  # end
29
30
  #
31
+ # # good
32
+ # x + x
33
+ # 1 << 1
34
+ #
30
35
  class BinaryOperatorWithIdenticalOperands < Base
31
36
  MSG = 'Binary operator `%<op>s` has identical operands.'
32
- MATH_OPERATORS = %i[+ - * / % ** << >> | ^].to_set.freeze
37
+ ALLOWED_MATH_OPERATORS = %i[+ * ** << >>].to_set.freeze
33
38
 
34
39
  def on_send(node)
35
40
  return unless node.binary_operation?
36
41
 
37
42
  lhs, operation, rhs = *node
38
- return if MATH_OPERATORS.include?(node.method_name) && lhs.basic_literal?
43
+ return if ALLOWED_MATH_OPERATORS.include?(node.method_name)
39
44
 
40
45
  add_offense(node, message: format(MSG, op: operation)) if lhs == rhs
41
46
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for deprecated constants.
7
+ #
8
+ # It has `DeprecatedConstants` config. If there is an alternative method, you can set
9
+ # alternative value as `Alternative`. And you can set the deprecated version as
10
+ # `DeprecatedVersion`. These options can be omitted if they are not needed.
11
+ #
12
+ # DeprecatedConstants:
13
+ # 'DEPRECATED_CONSTANT':
14
+ # Alternative: 'alternative_value'
15
+ # DeprecatedVersion: 'deprecated_version'
16
+ #
17
+ # By default, `NIL`, `TRUE`, `FALSE` and `Random::DEFAULT` are configured.
18
+ #
19
+ # @example
20
+ #
21
+ # # bad
22
+ # NIL
23
+ # TRUE
24
+ # FALSE
25
+ # Random::DEFAULT # Return value of Ruby 2 is `Random` instance, Ruby 3.0 is `Random` class.
26
+ #
27
+ # # good
28
+ # nil
29
+ # true
30
+ # false
31
+ # Random.new # `::DEFAULT` has been deprecated in Ruby 3, `.new` is compatible with Ruby 2.
32
+ #
33
+ class DeprecatedConstants < Base
34
+ extend AutoCorrector
35
+
36
+ SUGGEST_GOOD_MSG = 'Use `%<good>s` instead of `%<bad>s`%<deprecated_message>s.'
37
+ DO_NOT_USE_MSG = 'Do not use `%<bad>s`%<deprecated_message>s.'
38
+
39
+ def on_const(node)
40
+ constant = node.absolute? ? consntant_name(node, node.short_name.to_s) : node.source
41
+ return unless (deprecated_constant = deprecated_constants[constant])
42
+
43
+ alternative = deprecated_constant['Alternative']
44
+ version = deprecated_constant['DeprecatedVersion']
45
+
46
+ add_offense(node, message: message(alternative, node.source, version)) do |corrector|
47
+ corrector.replace(node, alternative)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def consntant_name(node, nested_constant_name)
54
+ return nested_constant_name unless node.namespace.const_type?
55
+
56
+ consntant_name(node.namespace, "#{node.namespace.short_name}::#{nested_constant_name}")
57
+ end
58
+
59
+ def message(good, bad, deprecated_version)
60
+ deprecated_message = ", deprecated since Ruby #{deprecated_version}" if deprecated_version
61
+
62
+ if good
63
+ format(SUGGEST_GOOD_MSG, good: good, bad: bad, deprecated_message: deprecated_message)
64
+ else
65
+ format(DO_NOT_USE_MSG, bad: bad, deprecated_message: deprecated_message)
66
+ end
67
+ end
68
+
69
+ def deprecated_constants
70
+ cop_config.fetch('DeprecatedConstants', {})
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -6,6 +6,15 @@ module RuboCop
6
6
  # This cop checks that there are no repeated bodies
7
7
  # within `if/unless`, `case-when` and `rescue` constructs.
8
8
  #
9
+ # With `IgnoreLiteralBranches: true`, branches are not registered
10
+ # as offenses if they return a basic literal value (string, symbol,
11
+ # integer, float, rational, complex, `true`, `false`, or `nil`), or
12
+ # return an array, hash, regexp or range that only contains one of
13
+ # the above basic literal values.
14
+ #
15
+ # With `IgnoreConstantBranches: true`, branches are not registered
16
+ # as offenses if they return a constant value.
17
+ #
9
18
  # @example
10
19
  # # bad
11
20
  # if foo
@@ -56,14 +65,33 @@ module RuboCop
56
65
  # handle_error
57
66
  # end
58
67
  #
68
+ # @example IgnoreLiteralBranches: true
69
+ # # good
70
+ # case size
71
+ # when "small" then 100
72
+ # when "medium" then 250
73
+ # when "large" then 1000
74
+ # else 250
75
+ # end
76
+ #
77
+ # @example IgnoreLiteralBranches: true
78
+ # # good
79
+ # case size
80
+ # when "small" then SMALL_SIZE
81
+ # when "medium" then MEDIUM_SIZE
82
+ # when "large" then LARGE_SIZE
83
+ # else MEDIUM_SIZE
84
+ # end
85
+ #
59
86
  class DuplicateBranch < Base
60
87
  include RescueNode
61
88
 
62
89
  MSG = 'Duplicate branch body detected.'
63
90
 
64
91
  def on_branching_statement(node)
65
- branches = node.branches.compact
66
- branches.each_with_object(Set.new) do |branch, previous|
92
+ branches(node).each_with_object(Set.new) do |branch, previous|
93
+ next unless consider_branch?(branch)
94
+
67
95
  add_offense(offense_range(branch)) unless previous.add?(branch)
68
96
  end
69
97
  end
@@ -87,6 +115,40 @@ module RuboCop
87
115
  parent.source_range
88
116
  end
89
117
  end
118
+
119
+ def branches(node)
120
+ node.branches.compact
121
+ end
122
+
123
+ def consider_branch?(branch)
124
+ return false if ignore_literal_branches? && literal_branch?(branch)
125
+ return false if ignore_constant_branches? && const_branch?(branch)
126
+
127
+ true
128
+ end
129
+
130
+ def ignore_literal_branches?
131
+ cop_config.fetch('IgnoreLiteralBranches', false)
132
+ end
133
+
134
+ def ignore_constant_branches?
135
+ cop_config.fetch('IgnoreConstantBranches', false)
136
+ end
137
+
138
+ def literal_branch?(branch) # rubocop:disable Metrics/CyclomaticComplexity
139
+ return false if !branch.literal? || branch.xstr_type?
140
+ return true if branch.basic_literal?
141
+
142
+ branch.each_descendant.all? do |node|
143
+ node.basic_literal? ||
144
+ node.pair_type? || # hash keys and values are contained within a `pair` node
145
+ (node.const_type? && ignore_constant_branches?)
146
+ end
147
+ end
148
+
149
+ def const_branch?(branch)
150
+ branch.const_type?
151
+ end
90
152
  end
91
153
  end
92
154
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks uses of lambda without a literal block.
7
+ # It emulates the following warning in Ruby 3.0:
8
+ #
9
+ # % ruby -vwe 'lambda(&proc {})'
10
+ # ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
11
+ # -e:1: warning: lambda without a literal block is deprecated; use the proc without
12
+ # lambda instead
13
+ #
14
+ # This way, proc object is never converted to lambda.
15
+ # Auto-correction replaces with compatible proc argument.
16
+ #
17
+ # @example
18
+ #
19
+ # # bad
20
+ # lambda(&proc { do_something })
21
+ # lambda(&Proc.new { do_something })
22
+ #
23
+ # # good
24
+ # proc { do_something }
25
+ # Proc.new { do_something }
26
+ # lambda { do_something } # If you use lambda.
27
+ #
28
+ class LambdaWithoutLiteralBlock < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'lambda without a literal block is deprecated; use the proc without lambda instead.'
32
+ RESTRICT_ON_SEND = %i[lambda].freeze
33
+
34
+ def on_send(node)
35
+ return if node.parent&.block_type? || !node.first_argument
36
+
37
+ add_offense(node) do |corrector|
38
+ corrector.replace(node, node.first_argument.source.delete('&'))
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -11,6 +11,11 @@ module RuboCop
11
11
  # that are hard to debug. To ensure this doesn't happen,
12
12
  # always sort the list.
13
13
  #
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.
16
+ #
17
+ # This cop will be deprecated and removed when supporting only Ruby 3.0 and higher.
18
+ #
14
19
  # @example
15
20
  #
16
21
  # # bad
@@ -23,8 +28,6 @@ module RuboCop
23
28
  # require file
24
29
  # end
25
30
  #
26
- # @example
27
- #
28
31
  # # bad
29
32
  # Dir.glob(Rails.root.join(__dir__, 'test', '*.rb')) do |file|
30
33
  # require file
@@ -35,28 +38,28 @@ module RuboCop
35
38
  # require file
36
39
  # end
37
40
  #
38
- # @example
39
- #
40
41
  # # bad
41
42
  # Dir['./lib/**/*.rb'].each(&method(:require))
42
43
  #
43
44
  # # good
44
45
  # Dir['./lib/**/*.rb'].sort.each(&method(:require))
45
46
  #
46
- # @example
47
- #
48
47
  # # bad
49
48
  # Dir.glob(Rails.root.join('test', '*.rb'), &method(:require))
50
49
  #
51
50
  # # good
52
51
  # Dir.glob(Rails.root.join('test', '*.rb')).sort.each(&method(:require))
53
52
  #
53
+ # # good - Respect intent if `sort` keyword option is specified in Ruby 3.0 or higher.
54
+ # Dir.glob(Rails.root.join(__dir__, 'test', '*.rb'), sort: false).each(&method(:require))
55
+ #
54
56
  class NonDeterministicRequireOrder < Base
55
57
  extend AutoCorrector
56
58
 
57
59
  MSG = 'Sort files before requiring them.'
58
60
 
59
61
  def on_block(node)
62
+ return if target_ruby_version >= 3.0
60
63
  return unless node.body
61
64
  return unless unsorted_dir_loop?(node.send_node)
62
65
 
@@ -70,6 +73,7 @@ module RuboCop
70
73
  end
71
74
 
72
75
  def on_block_pass(node)
76
+ return if target_ruby_version >= 3.0
73
77
  return unless method_require?(node)
74
78
  return unless unsorted_dir_pass?(node.parent)
75
79
 
@@ -25,12 +25,18 @@ module RuboCop
25
25
  # '10'.to_i
26
26
  # '10.2'.to_f
27
27
  # '10'.to_c
28
+ # ['1', '2', '3'].map(&:to_i)
29
+ # foo.try(:to_f)
30
+ # bar.send(:to_c)
28
31
  #
29
32
  # # good
30
33
  #
31
34
  # Integer('10', 10)
32
35
  # Float('10.2')
33
36
  # Complex('10')
37
+ # ['1', '2', '3'].map { |i| Integer(i, 10) }
38
+ # foo.try { |i| Float(i) }
39
+ # bar.send { |i| Complex(i) }
34
40
  #
35
41
  # @example IgnoredMethods: [minutes]
36
42
  #
@@ -52,22 +58,33 @@ module RuboCop
52
58
  }.freeze
53
59
  MSG = 'Replace unsafe number conversion with number '\
54
60
  'class parsing, instead of using '\
55
- '%<number_object>s.%<to_method>s, use stricter '\
61
+ '%<current>s, use stricter '\
56
62
  '%<corrected_method>s.'
57
- RESTRICT_ON_SEND = CONVERSION_METHOD_CLASS_MAPPING.keys.freeze
63
+ METHODS = CONVERSION_METHOD_CLASS_MAPPING.keys.map(&:inspect).join(' ')
58
64
 
59
65
  def_node_matcher :to_method, <<~PATTERN
60
- (send $_ ${:to_i :to_f :to_c})
66
+ (send $_ ${#{METHODS}})
67
+ PATTERN
68
+
69
+ def_node_matcher :to_method_symbol, <<~PATTERN
70
+ {(send _ $_ ${(sym ${#{METHODS}})} ...)
71
+ (send _ $_ ${(block_pass (sym ${#{METHODS}}))} ...)}
61
72
  PATTERN
62
73
 
63
74
  def on_send(node)
75
+ handle_conversion_method(node)
76
+ handle_as_symbol(node)
77
+ end
78
+
79
+ private
80
+
81
+ def handle_conversion_method(node)
64
82
  to_method(node) do |receiver, to_method|
65
83
  next if receiver.nil? || ignore_receiver?(receiver)
66
84
 
67
85
  message = format(
68
86
  MSG,
69
- number_object: receiver.source,
70
- to_method: to_method,
87
+ current: "#{receiver.source}.#{to_method}",
71
88
  corrected_method: correct_method(node, receiver)
72
89
  )
73
90
  add_offense(node, message: message) do |corrector|
@@ -76,13 +93,31 @@ module RuboCop
76
93
  end
77
94
  end
78
95
 
79
- private
96
+ def handle_as_symbol(node)
97
+ to_method_symbol(node) do |receiver, sym_node, to_method|
98
+ next if receiver.nil?
99
+
100
+ message = format(
101
+ MSG,
102
+ current: sym_node.source,
103
+ corrected_method: correct_sym_method(to_method)
104
+ )
105
+ add_offense(node, message: message) do |corrector|
106
+ corrector.replace(sym_node, correct_sym_method(to_method))
107
+ end
108
+ end
109
+ end
80
110
 
81
111
  def correct_method(node, receiver)
82
112
  format(CONVERSION_METHOD_CLASS_MAPPING[node.method_name],
83
113
  number_object: receiver.source)
84
114
  end
85
115
 
116
+ def correct_sym_method(to_method)
117
+ body = format(CONVERSION_METHOD_CLASS_MAPPING[to_method], number_object: 'i')
118
+ "{ |i| #{body} }"
119
+ end
120
+
86
121
  def ignore_receiver?(receiver)
87
122
  if receiver.send_type? && ignored_method?(receiver.method_name)
88
123
  true