rubocop 1.1.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -12
  3. data/config/default.yml +113 -16
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop.rb +9 -0
  6. data/lib/rubocop/cli/command/execute_runner.rb +26 -11
  7. data/lib/rubocop/config_loader.rb +14 -5
  8. data/lib/rubocop/config_regeneration.rb +1 -1
  9. data/lib/rubocop/cop/bundler/duplicated_gem.rb +3 -3
  10. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  11. data/lib/rubocop/cop/commissioner.rb +1 -1
  12. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  13. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
  14. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
  15. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  16. data/lib/rubocop/cop/generator.rb +2 -9
  17. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  18. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
  19. data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
  20. data/lib/rubocop/cop/layout/class_structure.rb +15 -3
  21. data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
  22. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +77 -7
  23. data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
  24. data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
  25. data/lib/rubocop/cop/layout/line_length.rb +8 -1
  26. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
  27. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  28. data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
  29. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  30. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
  31. data/lib/rubocop/cop/lint/debugger.rb +17 -27
  32. data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
  33. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
  34. data/lib/rubocop/cop/lint/else_layout.rb +29 -3
  35. data/lib/rubocop/cop/lint/empty_block.rb +38 -2
  36. data/lib/rubocop/cop/lint/empty_class.rb +93 -0
  37. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +22 -4
  38. data/lib/rubocop/cop/lint/loop.rb +4 -4
  39. data/lib/rubocop/cop/lint/missing_super.rb +7 -4
  40. data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
  41. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
  42. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
  43. data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
  44. data/lib/rubocop/cop/lint/to_enum_arguments.rb +6 -15
  45. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +13 -4
  46. data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
  47. data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
  48. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  49. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  50. data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
  51. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  52. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
  53. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
  54. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +11 -1
  55. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
  56. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
  57. data/lib/rubocop/cop/naming/variable_number.rb +98 -8
  58. data/lib/rubocop/cop/style/and_or.rb +1 -3
  59. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
  60. data/lib/rubocop/cop/style/case_like_if.rb +0 -4
  61. data/lib/rubocop/cop/style/collection_compact.rb +91 -0
  62. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +107 -5
  63. data/lib/rubocop/cop/style/documentation.rb +12 -1
  64. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  65. data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
  66. data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
  67. data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
  68. data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -3
  69. data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
  70. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
  71. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  72. data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
  73. data/lib/rubocop/cop/style/multiple_comparison.rb +3 -2
  74. data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
  75. data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
  76. data/lib/rubocop/cop/style/raise_args.rb +21 -6
  77. data/lib/rubocop/cop/style/redundant_argument.rb +73 -0
  78. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  79. data/lib/rubocop/cop/style/static_class.rb +97 -0
  80. data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
  81. data/lib/rubocop/cop/util.rb +5 -1
  82. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  83. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  84. data/lib/rubocop/ext/regexp_node.rb +10 -5
  85. data/lib/rubocop/ext/regexp_parser.rb +9 -2
  86. data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
  87. data/lib/rubocop/formatter/formatter_set.rb +1 -0
  88. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
  89. data/lib/rubocop/options.rb +7 -0
  90. data/lib/rubocop/rake_task.rb +2 -2
  91. data/lib/rubocop/runner.rb +1 -1
  92. data/lib/rubocop/target_finder.rb +1 -1
  93. data/lib/rubocop/target_ruby.rb +65 -1
  94. data/lib/rubocop/version.rb +1 -1
  95. metadata +14 -8
  96. data/bin/console +0 -10
  97. data/bin/rubocop-profile +0 -32
  98. data/bin/setup +0 -7
@@ -34,6 +34,7 @@ module RuboCop
34
34
  # this is rarely a problem in practice.
35
35
  class DoubleNegation < Base
36
36
  include ConfigurableEnforcedStyle
37
+ extend AutoCorrector
37
38
 
38
39
  MSG = 'Avoid the use of double negation (`!!`).'
39
40
  RESTRICT_ON_SEND = %i[!].freeze
@@ -44,7 +45,11 @@ module RuboCop
44
45
  return unless double_negative?(node) && node.prefix_bang?
45
46
  return if style == :allowed_in_returns && allowed_in_returns?(node)
46
47
 
47
- add_offense(node.loc.selector)
48
+ location = node.loc.selector
49
+ add_offense(location) do |corrector|
50
+ corrector.remove(location)
51
+ corrector.insert_after(node, '.nil?')
52
+ end
48
53
  end
49
54
 
50
55
  private
@@ -97,10 +97,10 @@ module RuboCop
97
97
  end
98
98
 
99
99
  def no_mixed_keys_check(pairs)
100
- if !sym_indices?(pairs)
101
- check(pairs, ':', MSG_NO_MIXED_KEYS)
102
- else
100
+ if sym_indices?(pairs)
103
101
  check(pairs, pairs.first.inverse_delimiter, MSG_NO_MIXED_KEYS)
102
+ else
103
+ check(pairs, ':', MSG_NO_MIXED_KEYS)
104
104
  end
105
105
  end
106
106
 
@@ -3,8 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # This cop checks for identical lines at the beginning or end of
7
- # each branch of a conditional statement.
6
+ # This cop checks for identical expressions at the beginning or end of
7
+ # each branch of a conditional expression. Such expressions should normally
8
+ # be placed outside the conditional expression - before or after it.
9
+ #
10
+ # NOTE: The cop is poorly named and some people might think that it actually
11
+ # checks for duplicated conditional branches. The name will probably be changed
12
+ # in a future major RuboCop release.
8
13
  #
9
14
  # @example
10
15
  # # bad
@@ -59,6 +59,9 @@ module RuboCop
59
59
  # end
60
60
  #
61
61
  class IfInsideElse < Base
62
+ include RangeHelp
63
+ extend AutoCorrector
64
+
62
65
  MSG = 'Convert `if` nested inside `else` to `elsif`.'
63
66
 
64
67
  def on_if(node)
@@ -69,11 +72,44 @@ module RuboCop
69
72
  return unless else_branch&.if_type? && else_branch&.if?
70
73
  return if allow_if_modifier_in_else_branch?(else_branch)
71
74
 
72
- add_offense(else_branch.loc.keyword)
75
+ add_offense(else_branch.loc.keyword) do |corrector|
76
+ autocorrect(corrector, else_branch)
77
+ end
73
78
  end
74
79
 
75
80
  private
76
81
 
82
+ def autocorrect(corrector, node)
83
+ if node.modifier_form?
84
+ correct_to_elsif_from_modifier_form(corrector, node)
85
+ end_range = node.parent.loc.end
86
+ else
87
+ correct_to_elsif_from_if_inside_else_form(corrector, node, node.condition)
88
+ end_range = node.loc.end
89
+ end
90
+ corrector.remove(range_by_whole_lines(end_range, include_final_newline: true))
91
+ corrector.remove(
92
+ range_by_whole_lines(node.if_branch.source_range, include_final_newline: true)
93
+ )
94
+ end
95
+
96
+ def correct_to_elsif_from_modifier_form(corrector, node)
97
+ corrector.replace(node.parent.loc.else, <<~RUBY.chop)
98
+ elsif #{node.condition.source}
99
+ #{indent(node.if_branch)}#{node.if_branch.source}
100
+ end
101
+ RUBY
102
+ end
103
+
104
+ def correct_to_elsif_from_if_inside_else_form(corrector, node, condition)
105
+ corrector.replace(node.parent.loc.else, "elsif #{condition.source}")
106
+ if_condition_range = range_between(
107
+ node.loc.keyword.begin_pos, condition.source_range.end_pos
108
+ )
109
+ corrector.replace(if_condition_range, node.if_branch.source)
110
+ corrector.remove(condition)
111
+ end
112
+
77
113
  def allow_if_modifier_in_else_branch?(else_branch)
78
114
  allow_if_modifier? && else_branch&.modifier_form?
79
115
  end
@@ -21,14 +21,18 @@ module RuboCop
21
21
  # Foo.do_something
22
22
  # end
23
23
  #
24
- # do_something_in_a_method_with_a_long_name(arg) if long_condition
24
+ # do_something_with_a_long_name(arg) if long_condition_that_prevents_code_fit_on_single_line
25
25
  #
26
26
  # # good
27
27
  # do_stuff(bar) if condition
28
28
  # Foo.do_something unless qux.empty?
29
29
  #
30
- # if long_condition
31
- # do_something_in_a_method_with_a_long_name(arg)
30
+ # if long_condition_that_prevents_code_fit_on_single_line
31
+ # do_something_with_a_long_name(arg)
32
+ # end
33
+ #
34
+ # if short_condition # a long comment that makes it too long if it were just a single line
35
+ # do_something
32
36
  # end
33
37
  class IfUnlessModifier < Base
34
38
  include StatementModifier
@@ -5,6 +5,10 @@ module RuboCop
5
5
  module Style
6
6
  # Use `Kernel#loop` for infinite loops.
7
7
  #
8
+ # This cop is marked as unsafe as the rule does not necessarily
9
+ # apply if the body might raise a `StopIteration` exception; contrary to
10
+ # other infinite loops, `Kernel#loop` silently rescues that and returns `nil`.
11
+ #
8
12
  # @example
9
13
  # # bad
10
14
  # while true
@@ -34,6 +34,10 @@ module RuboCop
34
34
  add_offense(node) do |corrector|
35
35
  if node.parent.find(&:kwoptarg_type?) == node
36
36
  corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ")
37
+
38
+ arguments = node.each_ancestor(:def, :defs).first.arguments
39
+ append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments)
40
+
37
41
  remove_kwargs(kwarg_nodes, corrector)
38
42
  end
39
43
  end
@@ -41,6 +45,14 @@ module RuboCop
41
45
 
42
46
  private
43
47
 
48
+ def append_newline_to_last_kwoptarg(arguments, corrector)
49
+ last_argument = arguments.last
50
+ return if last_argument.kwrestarg_type? || last_argument.blockarg_type?
51
+
52
+ last_kwoptarg = arguments.reverse.find(&:kwoptarg_type?)
53
+ corrector.insert_after(last_kwoptarg, "\n")
54
+ end
55
+
44
56
  def remove_kwargs(kwarg_nodes, corrector)
45
57
  kwarg_nodes.each do |kwarg|
46
58
  with_space = range_with_surrounding_space(range: kwarg.source_range)
@@ -74,7 +74,7 @@ module RuboCop
74
74
  parent &&
75
75
  (logical_operator?(parent) ||
76
76
  parent.send_type? &&
77
- parent.arguments.any?(&method(:logical_operator?)))
77
+ parent.arguments.any? { |argument| logical_operator?(argument) })
78
78
  end
79
79
 
80
80
  def call_in_optional_arguments?(node)
@@ -110,7 +110,7 @@ module RuboCop
110
110
  def hash_literal_in_arguments?(node)
111
111
  node.arguments.any? do |n|
112
112
  hash_literal?(n) ||
113
- n.send_type? && node.descendants.any?(&method(:hash_literal?))
113
+ n.send_type? && node.descendants.any? { |descendant| hash_literal?(descendant) }
114
114
  end
115
115
  end
116
116
 
@@ -135,10 +135,6 @@ module RuboCop
135
135
 
136
136
  "#{node.method_name} #{mixin_names.join(', ')}"
137
137
  end
138
-
139
- def indent(node)
140
- ' ' * node.loc.column
141
- end
142
138
  end
143
139
  end
144
140
  end
@@ -48,6 +48,7 @@ module RuboCop
48
48
 
49
49
  def on_new_investigation
50
50
  @compared_elements = []
51
+ @allowed_method_comparison = false
51
52
  end
52
53
 
53
54
  def on_or(node)
@@ -55,6 +56,7 @@ module RuboCop
55
56
 
56
57
  return unless node == root_of_or_node
57
58
  return unless nested_variable_comparison?(root_of_or_node)
59
+ return if @allowed_method_comparison
58
60
 
59
61
  add_offense(node) do |corrector|
60
62
  elements = @compared_elements.join(', ')
@@ -95,8 +97,7 @@ module RuboCop
95
97
  return [variable_name(var1), variable_name(var2)]
96
98
  end
97
99
  if (var, obj = simple_comparison_lhs?(node)) || (obj, var = simple_comparison_rhs?(node))
98
- return [] if allow_method_comparison? && obj.send_type?
99
-
100
+ @allowed_method_comparison = true if allow_method_comparison? && obj.send_type?
100
101
  @compared_elements << obj.source
101
102
  return [variable_name(var)]
102
103
  end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for uses of `if-else` and ternary operators with a negated condition
7
+ # which can be simplified by inverting condition and swapping branches.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # if !x
12
+ # do_something
13
+ # else
14
+ # do_something_else
15
+ # end
16
+ #
17
+ # # good
18
+ # if x
19
+ # do_something_else
20
+ # else
21
+ # do_something
22
+ # end
23
+ #
24
+ # # bad
25
+ # !x ? do_something : do_something_else
26
+ #
27
+ # # good
28
+ # x ? do_something_else : do_something
29
+ #
30
+ class NegatedIfElseCondition < Base
31
+ include RangeHelp
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Invert the negated condition and swap the %<type>s branches.'
35
+
36
+ NEGATED_EQUALITY_METHODS = %i[!= !~].freeze
37
+
38
+ def_node_matcher :double_negation?, '(send (send _ :!) :!)'
39
+
40
+ def self.autocorrect_incompatible_with
41
+ [Style::InverseMethods, Style::Not]
42
+ end
43
+
44
+ def on_new_investigation
45
+ @corrected_nodes = nil
46
+ end
47
+
48
+ def on_if(node)
49
+ return unless if_else?(node)
50
+
51
+ condition = node.condition
52
+ return if double_negation?(condition) || !negated_condition?(condition)
53
+
54
+ type = node.ternary? ? 'ternary' : 'if-else'
55
+ add_offense(node, message: format(MSG, type: type)) do |corrector|
56
+ unless corrected_ancestor?(node)
57
+ correct_negated_condition(corrector, condition)
58
+ swap_branches(corrector, node)
59
+
60
+ @corrected_nodes ||= Set.new.compare_by_identity
61
+ @corrected_nodes.add(node)
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def if_else?(node)
69
+ else_branch = node.else_branch
70
+ !node.elsif? && else_branch && (!else_branch.if_type? || !else_branch.elsif?)
71
+ end
72
+
73
+ def negated_condition?(node)
74
+ node.send_type? &&
75
+ (node.negation_method? || NEGATED_EQUALITY_METHODS.include?(node.method_name))
76
+ end
77
+
78
+ def corrected_ancestor?(node)
79
+ node.each_ancestor(:if).any? { |ancestor| @corrected_nodes&.include?(ancestor) }
80
+ end
81
+
82
+ def correct_negated_condition(corrector, node)
83
+ receiver, method_name, rhs = *node
84
+ replacement =
85
+ if node.negation_method?
86
+ receiver.source
87
+ else
88
+ inverted_method = method_name.to_s.sub('!', '=')
89
+ "#{receiver.source} #{inverted_method} #{rhs.source}"
90
+ end
91
+
92
+ corrector.replace(node, replacement)
93
+ end
94
+
95
+ def swap_branches(corrector, node)
96
+ if node.if_branch.nil?
97
+ corrector.remove(range_by_whole_lines(node.loc.else, include_final_newline: true))
98
+ else
99
+ corrector.replace(node.if_branch, node.else_branch.source)
100
+ corrector.replace(node.else_branch, node.if_branch.source)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for lambdas that always return nil, which can be replaced
7
+ # with an empty lambda instead.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # -> { nil }
12
+ #
13
+ # lambda do
14
+ # next nil
15
+ # end
16
+ #
17
+ # # good
18
+ # -> {}
19
+ #
20
+ # lambda do
21
+ # end
22
+ #
23
+ # -> (x) { nil if x }
24
+ #
25
+ class NilLambda < Base
26
+ extend AutoCorrector
27
+ include RangeHelp
28
+
29
+ MSG = 'Use an empty lambda instead of always returning nil.'
30
+
31
+ def_node_matcher :nil_return?, <<~PATTERN
32
+ { ({return next break} nil) (nil) }
33
+ PATTERN
34
+
35
+ def on_block(node)
36
+ return unless node.lambda?
37
+ return unless nil_return?(node.body)
38
+
39
+ add_offense(node) do |corrector|
40
+ range = if node.single_line?
41
+ range_with_surrounding_space(range: node.body.loc.expression)
42
+ else
43
+ range_by_whole_lines(node.body.loc.expression, include_final_newline: true)
44
+ end
45
+
46
+ corrector.remove(range)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -13,25 +13,32 @@ module RuboCop
13
13
  # will also suggest constructing error objects when the exception is
14
14
  # passed multiple arguments.
15
15
  #
16
+ # The exploded style has an `AllowedCompactTypes` configuration
17
+ # option that takes an Array of exception name Strings.
18
+ #
16
19
  # @example EnforcedStyle: exploded (default)
17
20
  # # bad
18
- # raise StandardError.new("message")
21
+ # raise StandardError.new('message')
19
22
  #
20
23
  # # good
21
- # raise StandardError, "message"
22
- # fail "message"
24
+ # raise StandardError, 'message'
25
+ # fail 'message'
23
26
  # raise MyCustomError.new(arg1, arg2, arg3)
24
27
  # raise MyKwArgError.new(key1: val1, key2: val2)
25
28
  #
29
+ # # With `AllowedCompactTypes` set to ['MyWrappedError']
30
+ # raise MyWrappedError.new(obj)
31
+ # raise MyWrappedError.new(obj), 'message'
32
+ #
26
33
  # @example EnforcedStyle: compact
27
34
  # # bad
28
- # raise StandardError, "message"
35
+ # raise StandardError, 'message'
29
36
  # raise RuntimeError, arg1, arg2, arg3
30
37
  #
31
38
  # # good
32
- # raise StandardError.new("message")
39
+ # raise StandardError.new('message')
33
40
  # raise MyCustomError.new(arg1, arg2, arg3)
34
- # fail "message"
41
+ # fail 'message'
35
42
  class RaiseArgs < Base
36
43
  include ConfigurableEnforcedStyle
37
44
  extend AutoCorrector
@@ -102,6 +109,8 @@ module RuboCop
102
109
  return unless first_arg.send_type? && first_arg.method?(:new)
103
110
  return if acceptable_exploded_args?(first_arg.arguments)
104
111
 
112
+ return if allowed_non_exploded_type?(first_arg)
113
+
105
114
  add_offense(node, message: format(EXPLODED_MSG, method: node.method_name)) do |corrector|
106
115
  replacement = correction_compact_to_exploded(node)
107
116
 
@@ -123,6 +132,12 @@ module RuboCop
123
132
  arg.hash_type? || arg.splat_type?
124
133
  end
125
134
 
135
+ def allowed_non_exploded_type?(arg)
136
+ type = arg.receiver.const_name
137
+
138
+ Array(cop_config['AllowedCompactTypes']).include?(type)
139
+ end
140
+
126
141
  def requires_parens?(parent)
127
142
  parent.and_type? || parent.or_type? ||
128
143
  parent.if_type? && parent.ternary?
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parser/current'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Style
8
+ # This cop checks for a redundant argument passed to certain methods.
9
+ #
10
+ # Limitations:
11
+ #
12
+ # 1. This cop matches for method names only and hence cannot tell apart
13
+ # methods with same name in different classes.
14
+ # 2. This cop is limited to methods with single parameter.
15
+ # 3. This cop is unsafe if certain special global variables (e.g. `$;`) are set.
16
+ # That depends on the nature of the target methods, of course.
17
+ #
18
+ # Method names and their redundant arguments can be configured like this:
19
+ #
20
+ # Methods:
21
+ # join: ''
22
+ # split: ' '
23
+ # foo: 2
24
+ #
25
+ # @example
26
+ # # bad
27
+ # array.join('')
28
+ # [1, 2, 3].join("")
29
+ # string.split(" ")
30
+ # "first\nsecond".split(" ")
31
+ # A.foo(2)
32
+ #
33
+ # # good
34
+ # array.join
35
+ # [1, 2, 3].join
36
+ # string.split
37
+ # "first second".split
38
+ # A.foo
39
+ class RedundantArgument < Cop
40
+ MSG = 'Argument %<arg>s is redundant because it is implied by default.'
41
+
42
+ def on_send(node)
43
+ return unless redundant_argument?(node)
44
+
45
+ add_offense(node, message: format(MSG, arg: node.arguments.first.source))
46
+ end
47
+
48
+ private
49
+
50
+ def redundant_argument?(node)
51
+ redundant_argument = redundant_arg_for_method(node.method_name.to_s)
52
+ return false if redundant_argument.nil?
53
+
54
+ node.arguments.first == redundant_argument
55
+ end
56
+
57
+ def redundant_arg_for_method(method_name)
58
+ return nil unless cop_config['Methods'].key?(method_name)
59
+
60
+ @mem ||= {}
61
+ @mem[method_name] ||=
62
+ begin
63
+ arg = cop_config['Methods'].fetch(method_name)
64
+ buffer = Parser::Source::Buffer.new('(string)', 1)
65
+ buffer.source = arg.inspect
66
+ builder = RuboCop::AST::Builder.new
67
+ Parser::CurrentRuby.new(builder).parse(buffer)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end