rubocop 1.0.0 → 1.3.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -16
  3. data/config/default.yml +141 -14
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop.rb +16 -0
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  7. data/lib/rubocop/comment_config.rb +1 -1
  8. data/lib/rubocop/config_loader.rb +7 -6
  9. data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
  10. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  11. data/lib/rubocop/cop/commissioner.rb +10 -10
  12. data/lib/rubocop/cop/corrector.rb +3 -1
  13. data/lib/rubocop/cop/force.rb +1 -1
  14. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
  16. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  17. data/lib/rubocop/cop/generator.rb +1 -1
  18. data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
  19. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
  21. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  22. data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
  23. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  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_inside_parens.rb +35 -13
  28. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  29. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +23 -2
  30. data/lib/rubocop/cop/lint/debugger.rb +17 -28
  31. data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
  32. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
  33. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  34. data/lib/rubocop/cop/lint/else_layout.rb +29 -3
  35. data/lib/rubocop/cop/lint/empty_block.rb +82 -0
  36. data/lib/rubocop/cop/lint/empty_class.rb +93 -0
  37. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  38. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +38 -6
  39. data/lib/rubocop/cop/lint/loop.rb +4 -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/number_conversion.rb +46 -13
  43. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  44. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
  45. data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
  46. data/lib/rubocop/cop/lint/to_enum_arguments.rb +95 -0
  47. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
  48. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  49. data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
  50. data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
  51. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  52. data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
  53. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  54. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
  55. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +11 -1
  56. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
  57. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
  58. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  59. data/lib/rubocop/cop/naming/variable_number.rb +98 -8
  60. data/lib/rubocop/cop/offense.rb +3 -3
  61. data/lib/rubocop/cop/style/and_or.rb +1 -3
  62. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  63. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
  64. data/lib/rubocop/cop/style/case_like_if.rb +0 -4
  65. data/lib/rubocop/cop/style/collection_compact.rb +91 -0
  66. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +162 -0
  67. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  68. data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
  69. data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
  70. data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
  71. data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -3
  72. data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
  73. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
  74. data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
  75. data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
  76. data/lib/rubocop/cop/style/negated_if_else_condition.rb +104 -0
  77. data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
  78. data/lib/rubocop/cop/style/raise_args.rb +21 -6
  79. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
  80. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  81. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  82. data/lib/rubocop/cop/style/static_class.rb +97 -0
  83. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  84. data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
  85. data/lib/rubocop/cop/team.rb +6 -1
  86. data/lib/rubocop/cop/util.rb +5 -1
  87. data/lib/rubocop/ext/regexp_node.rb +17 -9
  88. data/lib/rubocop/ext/regexp_parser.rb +84 -0
  89. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  90. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
  91. data/lib/rubocop/magic_comment.rb +2 -2
  92. data/lib/rubocop/options.rb +2 -0
  93. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  94. data/lib/rubocop/target_ruby.rb +57 -1
  95. data/lib/rubocop/version.rb +1 -1
  96. metadata +21 -5
@@ -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)
@@ -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
@@ -4,7 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop checks against comparing a variable with multiple items, where
7
- # `Array#include?` could be used instead to avoid code repetition.
7
+ # `Array#include?`, `Set#include?` or a `case` could be used instead
8
+ # to avoid code repetition.
9
+ # It accepts comparisons of multiple method calls to avoid unnecessary method calls
10
+ # by default. It can be configured by `AllowMethodComparison` option.
8
11
  #
9
12
  # @example
10
13
  # # bad
@@ -14,25 +17,63 @@ module RuboCop
14
17
  # # good
15
18
  # a = 'a'
16
19
  # foo if ['a', 'b', 'c'].include?(a)
20
+ #
21
+ # VALUES = Set['a', 'b', 'c'].freeze
22
+ # # elsewhere...
23
+ # foo if VALUES.include?(a)
24
+ #
25
+ # case foo
26
+ # when 'a', 'b', 'c' then foo
27
+ # # ...
28
+ # end
29
+ #
30
+ # # accepted (but consider `case` as above)
31
+ # foo if a == b.lightweight || a == b.heavyweight
32
+ #
33
+ # @example AllowMethodComparison: true (default)
34
+ # # good
35
+ # foo if a == b.lightweight || a == b.heavyweight
36
+ #
37
+ # @example AllowMethodComparison: false
38
+ # # bad
39
+ # foo if a == b.lightweight || a == b.heavyweight
40
+ #
41
+ # # good
42
+ # foo if [b.lightweight, b.heavyweight].include?(a)
17
43
  class MultipleComparison < Base
44
+ extend AutoCorrector
45
+
18
46
  MSG = 'Avoid comparing a variable with multiple items ' \
19
47
  'in a conditional, use `Array#include?` instead.'
20
48
 
49
+ def on_new_investigation
50
+ @compared_elements = []
51
+ @allowed_method_comparison = false
52
+ end
53
+
21
54
  def on_or(node)
22
55
  root_of_or_node = root_of_or_node(node)
23
56
 
24
57
  return unless node == root_of_or_node
25
58
  return unless nested_variable_comparison?(root_of_or_node)
59
+ return if @allowed_method_comparison
26
60
 
27
- add_offense(node)
61
+ add_offense(node) do |corrector|
62
+ elements = @compared_elements.join(', ')
63
+ prefer_method = "[#{elements}].include?(#{variables_in_node(node).first})"
64
+
65
+ corrector.replace(node, prefer_method)
66
+ end
28
67
  end
29
68
 
30
69
  private
31
70
 
32
71
  def_node_matcher :simple_double_comparison?, '(send $lvar :== $lvar)'
33
- def_node_matcher :simple_comparison?, <<~PATTERN
34
- {(send $lvar :== _)
35
- (send _ :== $lvar)}
72
+ def_node_matcher :simple_comparison_lhs?, <<~PATTERN
73
+ (send $lvar :== $_)
74
+ PATTERN
75
+ def_node_matcher :simple_comparison_rhs?, <<~PATTERN
76
+ (send $_ :== $lvar)
36
77
  PATTERN
37
78
 
38
79
  def nested_variable_comparison?(node)
@@ -55,9 +96,12 @@ module RuboCop
55
96
  simple_double_comparison?(node) do |var1, var2|
56
97
  return [variable_name(var1), variable_name(var2)]
57
98
  end
58
- simple_comparison?(node) do |var|
99
+ if (var, obj = simple_comparison_lhs?(node)) || (obj, var = simple_comparison_rhs?(node))
100
+ @allowed_method_comparison = true if allow_method_comparison? && obj.send_type?
101
+ @compared_elements << obj.source
59
102
  return [variable_name(var)]
60
103
  end
104
+
61
105
  []
62
106
  end
63
107
 
@@ -74,7 +118,7 @@ module RuboCop
74
118
  end
75
119
 
76
120
  def comparison?(node)
77
- simple_comparison?(node) || nested_comparison?(node)
121
+ simple_comparison_lhs?(node) || simple_comparison_rhs?(node) || nested_comparison?(node)
78
122
  end
79
123
 
80
124
  def root_of_or_node(or_node)
@@ -86,6 +130,10 @@ module RuboCop
86
130
  or_node
87
131
  end
88
132
  end
133
+
134
+ def allow_method_comparison?
135
+ cop_config.fetch('AllowMethodComparison', true)
136
+ end
89
137
  end
90
138
  end
91
139
  end
@@ -0,0 +1,104 @@
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 self.autocorrect_incompatible_with
39
+ [Style::InverseMethods, Style::Not]
40
+ end
41
+
42
+ def on_new_investigation
43
+ @corrected_nodes = nil
44
+ end
45
+
46
+ def on_if(node)
47
+ return unless if_else?(node)
48
+
49
+ condition = node.condition
50
+ return unless negated_condition?(condition)
51
+
52
+ type = node.ternary? ? 'ternary' : 'if-else'
53
+ add_offense(node, message: format(MSG, type: type)) do |corrector|
54
+ unless corrected_ancestor?(node)
55
+ correct_negated_condition(corrector, condition)
56
+ swap_branches(corrector, node)
57
+
58
+ @corrected_nodes ||= Set.new.compare_by_identity
59
+ @corrected_nodes.add(node)
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def if_else?(node)
67
+ else_branch = node.else_branch
68
+ !node.elsif? && else_branch && (!else_branch.if_type? || !else_branch.elsif?)
69
+ end
70
+
71
+ def negated_condition?(node)
72
+ node.send_type? &&
73
+ (node.negation_method? || NEGATED_EQUALITY_METHODS.include?(node.method_name))
74
+ end
75
+
76
+ def corrected_ancestor?(node)
77
+ node.each_ancestor(:if).any? { |ancestor| @corrected_nodes&.include?(ancestor) }
78
+ end
79
+
80
+ def correct_negated_condition(corrector, node)
81
+ receiver, method_name, rhs = *node
82
+ replacement =
83
+ if node.negation_method?
84
+ receiver.source
85
+ else
86
+ inverted_method = method_name.to_s.sub('!', '=')
87
+ "#{receiver.source} #{inverted_method} #{rhs.source}"
88
+ end
89
+
90
+ corrector.replace(node, replacement)
91
+ end
92
+
93
+ def swap_branches(corrector, node)
94
+ if node.if_branch.nil?
95
+ corrector.remove(range_by_whole_lines(node.loc.else, include_final_newline: true))
96
+ else
97
+ corrector.replace(node.if_branch, node.else_branch.source)
98
+ corrector.replace(node.else_branch, node.if_branch.source)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ 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