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
@@ -31,22 +31,12 @@ module RuboCop
31
31
  MSG = 'Duplicate `when` condition detected.'
32
32
 
33
33
  def on_case(case_node)
34
- case_node.when_branches.each_with_object([]) do |when_node, previous|
34
+ case_node.when_branches.each_with_object(Set.new) do |when_node, previous|
35
35
  when_node.each_condition do |condition|
36
- next unless repeated_condition?(previous, condition)
37
-
38
- add_offense(condition)
36
+ add_offense(condition) unless previous.add?(condition)
39
37
  end
40
-
41
- previous.push(when_node.conditions)
42
38
  end
43
39
  end
44
-
45
- private
46
-
47
- def repeated_condition?(previous, condition)
48
- previous.any? { |c| c.include?(condition) }
49
- end
50
40
  end
51
41
  end
52
42
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for duplicate elements in Regexp character classes.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # r = /[xyx]/
12
+ #
13
+ # # bad
14
+ # r = /[0-9x0-9]/
15
+ #
16
+ # # good
17
+ # r = /[xy]/
18
+ #
19
+ # # good
20
+ # r = /[0-9x]/
21
+ class DuplicateRegexpCharacterClassElement < Base
22
+ include RangeHelp
23
+ extend AutoCorrector
24
+
25
+ MSG_REPEATED_ELEMENT = 'Duplicate element inside regexp character class'
26
+
27
+ def on_regexp(node)
28
+ each_repeated_character_class_element_loc(node) do |loc|
29
+ add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector|
30
+ corrector.remove(loc)
31
+ end
32
+ end
33
+ end
34
+
35
+ def each_repeated_character_class_element_loc(node)
36
+ node.parsed_tree&.each_expression do |expr|
37
+ next if expr.type != :set || expr.token == :intersection
38
+
39
+ seen = Set.new
40
+
41
+ expr.expressions.each do |child|
42
+ next if within_interpolation?(node, child)
43
+
44
+ child_source = child.to_s
45
+
46
+ yield child.expression if seen.include?(child_source)
47
+
48
+ seen << child_source
49
+ end
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # Since we blank interpolations with a space for every char of the interpolation, we would
56
+ # mark every space (except the first) as duplicate if we do not skip regexp_parser nodes
57
+ # that are within an interpolation.
58
+ def within_interpolation?(node, child)
59
+ parse_tree_child_loc = child.expression
60
+
61
+ interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
62
+ end
63
+
64
+ def interpolation_locs(node)
65
+ @interpolation_locs ||= {}
66
+
67
+ # Cache by loc, not by regexp content, as content can be repeated in multiple patterns
68
+ key = node.loc
69
+
70
+ @interpolation_locs[key] ||= node.children.select(&:begin_type?).map do |interpolation|
71
+ interpolation.loc.expression
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -3,10 +3,14 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cop checks for odd else block layout - like
7
- # having an expression on the same line as the else keyword,
6
+ # This cop checks for odd `else` block layout - like
7
+ # having an expression on the same line as the `else` keyword,
8
8
  # which is usually a mistake.
9
9
  #
10
+ # Its auto-correction tweaks layout to keep the syntax. So, this auto-correction
11
+ # is compatible correction for bad case syntax, but if your code makes a mistake
12
+ # with `elsif` and `else`, you will have to correct it manually.
13
+ #
10
14
  # @example
11
15
  #
12
16
  # # bad
@@ -21,13 +25,25 @@ module RuboCop
21
25
  #
22
26
  # # good
23
27
  #
28
+ # # This code is compatible with the bad case. It will be auto-corrected like this.
24
29
  # if something
25
30
  # # ...
26
31
  # else
27
32
  # do_this
28
33
  # do_that
29
34
  # end
35
+ #
36
+ # # This code is incompatible with the bad case.
37
+ # # If `do_this` is a condition, `elsif` should be used instead of `else`.
38
+ # if something
39
+ # # ...
40
+ # elsif do_this
41
+ # do_that
42
+ # end
30
43
  class ElseLayout < Base
44
+ include RangeHelp
45
+ extend AutoCorrector
46
+
31
47
  MSG = 'Odd `else` layout detected. Did you mean to use `elsif`?'
32
48
 
33
49
  def on_if(node)
@@ -58,7 +74,17 @@ module RuboCop
58
74
  return unless first_else
59
75
  return unless first_else.source_range.line == node.loc.else.line
60
76
 
61
- add_offense(first_else)
77
+ add_offense(first_else) do |corrector|
78
+ autocorrect(corrector, node, first_else)
79
+ end
80
+ end
81
+
82
+ def autocorrect(corrector, node, first_else)
83
+ corrector.insert_after(node.loc.else, "\n")
84
+
85
+ blank_range = range_between(node.loc.else.end_pos, first_else.loc.expression.begin_pos)
86
+ indentation = indent(node.else_branch.children[1])
87
+ corrector.replace(blank_range, indentation)
62
88
  end
63
89
  end
64
90
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for blocks without a body.
7
+ # Such empty blocks are typically an oversight or we should provide a comment
8
+ # be clearer what we're aiming for.
9
+ #
10
+ # Empty lambdas are ignored by default.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # items.each { |item| }
15
+ #
16
+ # # good
17
+ # items.each { |item| puts item }
18
+ #
19
+ # @example AllowComments: true (default)
20
+ # # good
21
+ # items.each do |item|
22
+ # # TODO: implement later (inner comment)
23
+ # end
24
+ #
25
+ # items.each { |item| } # TODO: implement later (inline comment)
26
+ #
27
+ # @example AllowComments: false
28
+ # # bad
29
+ # items.each do |item|
30
+ # # TODO: implement later (inner comment)
31
+ # end
32
+ #
33
+ # items.each { |item| } # TODO: implement later (inline comment)
34
+ #
35
+ # @example AllowEmptyLambdas: true (default)
36
+ # # good
37
+ # allow(subject).to receive(:callable).and_return(-> {})
38
+ #
39
+ # placeholder = lambda do
40
+ # end
41
+ # (callable || placeholder).call
42
+ #
43
+ # @example AllowEmptyLambdas: false
44
+ # # bad
45
+ # allow(subject).to receive(:callable).and_return(-> {})
46
+ #
47
+ # placeholder = lambda do
48
+ # end
49
+ # (callable || placeholder).call
50
+ #
51
+ class EmptyBlock < Base
52
+ MSG = 'Empty block detected.'
53
+
54
+ def on_block(node)
55
+ return if node.body
56
+ return if allow_empty_lambdas? && node.lambda?
57
+ return if cop_config['AllowComments'] && allow_comment?(node)
58
+
59
+ add_offense(node)
60
+ end
61
+
62
+ private
63
+
64
+ def allow_comment?(node)
65
+ return false unless processed_source.contains_comment?(node.source_range)
66
+
67
+ line_comment = processed_source.comment_at_line(node.source_range.line)
68
+ !line_comment || !comment_disables_cop?(line_comment.loc.expression.source)
69
+ end
70
+
71
+ def allow_empty_lambdas?
72
+ cop_config['AllowEmptyLambdas']
73
+ end
74
+
75
+ def comment_disables_cop?(comment)
76
+ regexp_pattern = "# rubocop : (disable|todo) ([^,],)* (all|#{cop_name})"
77
+ Regexp.new(regexp_pattern.gsub(' ', '\s*')).match?(comment)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for classes and metaclasses without a body.
7
+ # Such empty classes and metaclasses are typically an oversight or we should provide a comment
8
+ # to be clearer what we're aiming for.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # class Foo
13
+ # end
14
+ #
15
+ # class Bar
16
+ # class << self
17
+ # end
18
+ # end
19
+ #
20
+ # class << obj
21
+ # end
22
+ #
23
+ # # good
24
+ # class Foo
25
+ # def do_something
26
+ # # ... code
27
+ # end
28
+ # end
29
+ #
30
+ # class Bar
31
+ # class << self
32
+ # attr_reader :bar
33
+ # end
34
+ # end
35
+ #
36
+ # class << obj
37
+ # attr_reader :bar
38
+ # end
39
+ #
40
+ # @example AllowComments: false (default)
41
+ # # bad
42
+ # class Foo
43
+ # # TODO: implement later
44
+ # end
45
+ #
46
+ # class Bar
47
+ # class << self
48
+ # # TODO: implement later
49
+ # end
50
+ # end
51
+ #
52
+ # class << obj
53
+ # # TODO: implement later
54
+ # end
55
+ #
56
+ # @example AllowComments: true
57
+ # # good
58
+ # class Foo
59
+ # # TODO: implement later
60
+ # end
61
+ #
62
+ # class Bar
63
+ # class << self
64
+ # # TODO: implement later
65
+ # end
66
+ # end
67
+ #
68
+ # class << obj
69
+ # # TODO: implement later
70
+ # end
71
+ #
72
+ class EmptyClass < Base
73
+ CLASS_MSG = 'Empty class detected.'
74
+ METACLASS_MSG = 'Empty metaclass detected.'
75
+
76
+ def on_class(node)
77
+ add_offense(node, message: CLASS_MSG) unless body_or_allowed_comment_lines?(node) ||
78
+ node.parent_class
79
+ end
80
+
81
+ def on_sclass(node)
82
+ add_offense(node, message: METACLASS_MSG) unless body_or_allowed_comment_lines?(node)
83
+ end
84
+
85
+ private
86
+
87
+ def body_or_allowed_comment_lines?(node)
88
+ node.body || (cop_config['AllowComments'] && comment_lines?(node))
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -3,8 +3,14 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cop looks for uses of flip-flop operator.
7
- # flip-flop operator is deprecated since Ruby 2.6.0.
6
+ # This cop looks for uses of flip-flop operator
7
+ # based on the Ruby Style Guide.
8
+ #
9
+ # Here is the history of flip-flops in Ruby.
10
+ # flip-flop operator is deprecated in Ruby 2.6.0 and
11
+ # the deprecation has been reverted by Ruby 2.7.0 and
12
+ # backported to Ruby 2.6.
13
+ # See: https://bugs.ruby-lang.org/issues/5400
8
14
  #
9
15
  # @example
10
16
  # # bad
@@ -27,21 +27,33 @@ module RuboCop
27
27
 
28
28
  def on_interpolation(begin_node)
29
29
  final_node = begin_node.children.last
30
- return unless final_node
31
- return if special_keyword?(final_node)
32
- return unless prints_as_self?(final_node)
30
+ return unless offending?(final_node)
31
+
32
+ # %W and %I split the content into words before expansion
33
+ # treating each interpolation as a word component, so
34
+ # interpolation should not be removed if the expanded value
35
+ # contains a space character.
36
+ expanded_value = autocorrected_value(final_node)
37
+ return if in_array_percent_literal?(begin_node) &&
38
+ /\s/.match?(expanded_value)
33
39
 
34
40
  add_offense(final_node) do |corrector|
35
41
  return if final_node.dstr_type? # nested, fixed in next iteration
36
42
 
37
- value = autocorrected_value(final_node)
38
-
39
- corrector.replace(final_node.parent, value)
43
+ corrector.replace(final_node.parent, expanded_value)
40
44
  end
41
45
  end
42
46
 
43
47
  private
44
48
 
49
+ def offending?(node)
50
+ node &&
51
+ !special_keyword?(node) &&
52
+ prints_as_self?(node) &&
53
+ # Special case for Layout/TrailingWhitespace
54
+ !(space_literal?(node) && ends_heredoc_line?(node))
55
+ end
56
+
45
57
  def special_keyword?(node)
46
58
  # handle strings like __FILE__
47
59
  (node.str_type? && !node.loc.respond_to?(:begin)) ||
@@ -92,6 +104,26 @@ module RuboCop
92
104
  (COMPOSITE.include?(node.type) &&
93
105
  node.children.all? { |child| prints_as_self?(child) })
94
106
  end
107
+
108
+ def space_literal?(node)
109
+ node.str_type? && node.value.blank?
110
+ end
111
+
112
+ def ends_heredoc_line?(node)
113
+ grandparent = node.parent.parent
114
+ return false unless grandparent&.dstr_type? && grandparent&.heredoc?
115
+
116
+ line = processed_source.lines[node.last_line - 1]
117
+ line.size == node.loc.last_column + 1
118
+ end
119
+
120
+ def in_array_percent_literal?(node)
121
+ parent = node.parent
122
+ return false unless parent.dstr_type? || parent.dsym_type?
123
+
124
+ grandparent = parent.parent
125
+ grandparent&.array_type? && grandparent&.percent_literal?
126
+ end
95
127
  end
96
128
  end
97
129
  end
@@ -5,6 +5,10 @@ module RuboCop
5
5
  module Lint
6
6
  # This cop checks for uses of `begin...end while/until something`.
7
7
  #
8
+ # The cop is marked as unsafe because behaviour can change in some cases, including
9
+ # if a local variable inside the loop body is accessed outside of it, or if the
10
+ # loop body raises a `StopIteration` exception (which `Kernel#loop` rescues).
11
+ #
8
12
  # @example
9
13
  #
10
14
  # # bad
@@ -76,10 +80,6 @@ module RuboCop
76
80
  conditional_keyword = node.while_post_type? ? 'unless' : 'if'
77
81
  "break #{conditional_keyword} #{node.condition.source}\n#{indent(node)}"
78
82
  end
79
-
80
- def indent(node)
81
- ' ' * node.loc.column
82
- end
83
83
  end
84
84
  end
85
85
  end