rubocop 1.0.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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