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
@@ -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?
@@ -19,6 +19,12 @@ module RuboCop
19
19
  # # good
20
20
  # r = /\s/
21
21
  #
22
+ # # bad
23
+ # r = %r{/[b]}
24
+ #
25
+ # # good
26
+ # r = %r{/b}
27
+ #
22
28
  # # good
23
29
  # r = /[ab]/
24
30
  class RedundantRegexpCharacterClass < Base
@@ -48,7 +54,7 @@ module RuboCop
48
54
  each_single_element_character_class(node) do |char_class|
49
55
  next unless redundant_single_element_character_class?(node, char_class)
50
56
 
51
- yield node.loc.begin.adjust(begin_pos: 1 + char_class.ts, end_pos: char_class.te)
57
+ yield char_class.loc.body
52
58
  end
53
59
  end
54
60
 
@@ -82,7 +82,7 @@ module RuboCop
82
82
 
83
83
  def each_escape(node)
84
84
  node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
85
- yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
85
+ yield(expr.text[1], expr.start_index, !char_class_depth.zero?) if expr.type == :escape
86
86
 
87
87
  if expr.type == :set
88
88
  char_class_depth + (event == :enter ? 1 : -1)
@@ -70,6 +70,9 @@ module RuboCop
70
70
  private
71
71
 
72
72
  def check_for_line_terminator_or_opener
73
+ # Make the obvious check first
74
+ return unless @processed_source.raw_source.include?(';')
75
+
73
76
  each_semicolon { |line, column| convention_on(line, column, true) }
74
77
  end
75
78
 
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for places where classes with only class methods can be
7
+ # replaced with a module. Classes should be used only when it makes sense to create
8
+ # instances out of them.
9
+ #
10
+ # This cop is marked as unsafe, because it is possible that this class is a parent
11
+ # for some other subclass, monkey-patched with instance methods or
12
+ # a dummy instance is instantiated from it somewhere.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # class SomeClass
17
+ # def self.some_method
18
+ # # body omitted
19
+ # end
20
+ #
21
+ # def self.some_other_method
22
+ # # body omitted
23
+ # end
24
+ # end
25
+ #
26
+ # # good
27
+ # module SomeModule
28
+ # module_function
29
+ #
30
+ # def some_method
31
+ # # body omitted
32
+ # end
33
+ #
34
+ # def some_other_method
35
+ # # body omitted
36
+ # end
37
+ # end
38
+ #
39
+ # # good - has instance method
40
+ # class SomeClass
41
+ # def instance_method; end
42
+ # def self.class_method; end
43
+ # end
44
+ #
45
+ class StaticClass < Base
46
+ include VisibilityHelp
47
+
48
+ MSG = 'Prefer modules to classes with only class methods.'
49
+
50
+ def on_class(class_node)
51
+ return if class_node.parent_class
52
+
53
+ add_offense(class_node) if class_convertible_to_module?(class_node)
54
+ end
55
+
56
+ private
57
+
58
+ def class_convertible_to_module?(class_node)
59
+ nodes = class_elements(class_node)
60
+ return false if nodes.empty?
61
+
62
+ nodes.all? do |node|
63
+ node_visibility(node) == :public &&
64
+ node.defs_type? ||
65
+ sclass_convertible_to_module?(node) ||
66
+ node.equals_asgn? ||
67
+ extend_call?(node)
68
+ end
69
+ end
70
+
71
+ def extend_call?(node)
72
+ node.send_type? && node.method?(:extend)
73
+ end
74
+
75
+ def sclass_convertible_to_module?(node)
76
+ return false unless node.sclass_type?
77
+
78
+ class_elements(node).all? do |child|
79
+ node_visibility(child) == :public && (child.def_type? || child.equals_asgn?)
80
+ end
81
+ end
82
+
83
+ def class_elements(class_node)
84
+ class_def = class_node.body
85
+
86
+ if !class_def
87
+ []
88
+ elsif class_def.begin_type?
89
+ class_def.children
90
+ else
91
+ [class_def]
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop enforces the use of shorthand-style swapping of 2 variables.
7
+ # Its autocorrection is marked as unsafe, because it can erroneously remove
8
+ # the temporary variable which is used later.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # tmp = x
13
+ # x = y
14
+ # y = tmp
15
+ #
16
+ # # good
17
+ # x, y = y, x
18
+ #
19
+ class SwapValues < Base
20
+ include RangeHelp
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Replace this and assignments at lines %<x_line>d '\
24
+ 'and %<y_line>d with `%<replacement>s`.'
25
+
26
+ SIMPLE_ASSIGNMENT_TYPES = %i[lvasgn ivasgn cvasgn gvasgn casgn].to_set.freeze
27
+
28
+ def on_asgn(node)
29
+ return if allowed_assignment?(node)
30
+
31
+ tmp_assign = node
32
+ x_assign, y_assign = *node.right_siblings.take(2)
33
+ return unless x_assign && y_assign && swapping_values?(tmp_assign, x_assign, y_assign)
34
+
35
+ add_offense(node, message: message(x_assign, y_assign)) do |corrector|
36
+ range = correction_range(tmp_assign, y_assign)
37
+ corrector.replace(range, replacement(x_assign))
38
+ end
39
+ end
40
+
41
+ SIMPLE_ASSIGNMENT_TYPES.each { |asgn_type| alias_method :"on_#{asgn_type}", :on_asgn }
42
+
43
+ private
44
+
45
+ def allowed_assignment?(node)
46
+ node.parent&.mlhs_type? || node.parent&.shorthand_asgn?
47
+ end
48
+
49
+ def swapping_values?(tmp_assign, x_assign, y_assign)
50
+ simple_assignment?(tmp_assign) &&
51
+ simple_assignment?(x_assign) &&
52
+ simple_assignment?(y_assign) &&
53
+ lhs(x_assign) == rhs(tmp_assign) &&
54
+ lhs(y_assign) == rhs(x_assign) &&
55
+ rhs(y_assign) == lhs(tmp_assign)
56
+ end
57
+
58
+ def simple_assignment?(node)
59
+ SIMPLE_ASSIGNMENT_TYPES.include?(node.type)
60
+ end
61
+
62
+ def message(x_assign, y_assign)
63
+ format(
64
+ MSG,
65
+ x_line: x_assign.first_line,
66
+ y_line: y_assign.first_line,
67
+ replacement: replacement(x_assign)
68
+ )
69
+ end
70
+
71
+ def replacement(x_assign)
72
+ x = lhs(x_assign)
73
+ y = rhs(x_assign)
74
+ "#{x}, #{y} = #{y}, #{x}"
75
+ end
76
+
77
+ def lhs(node)
78
+ case node.type
79
+ when :casgn
80
+ namespace, name, = *node
81
+ if namespace
82
+ "#{namespace.const_name}::#{name}"
83
+ else
84
+ name.to_s
85
+ end
86
+ else
87
+ node.children[0].to_s
88
+ end
89
+ end
90
+
91
+ def rhs(node)
92
+ case node.type
93
+ when :casgn
94
+ node.children[2].source
95
+ else
96
+ node.children[1].source
97
+ end
98
+ end
99
+
100
+ def correction_range(tmp_assign, y_assign)
101
+ range_by_whole_lines(
102
+ range_between(tmp_assign.source_range.begin_pos, y_assign.source_range.end_pos)
103
+ )
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -24,6 +24,15 @@ module RuboCop
24
24
  #
25
25
  # # good
26
26
  # x += 1 until x > 10
27
+ #
28
+ # @example
29
+ # # bad
30
+ # x += 100 while x < 500 # a long comment that makes code too long if it were a single line
31
+ #
32
+ # # good
33
+ # while x < 500 # a long comment that makes code too long if it were a single line
34
+ # x += 100
35
+ # end
27
36
  class WhileUntilModifier < Base
28
37
  include StatementModifier
29
38
  extend AutoCorrector
@@ -99,7 +99,12 @@ module RuboCop
99
99
  def self.forces_for(cops)
100
100
  needed = Hash.new { |h, k| h[k] = [] }
101
101
  cops.each do |cop|
102
- Array(cop.class.joining_forces).each { |force| needed[force] << cop }
102
+ forces = cop.class.joining_forces
103
+ if forces.is_a?(Array)
104
+ forces.each { |force| needed[force] << cop }
105
+ elsif forces
106
+ needed[forces] << cop
107
+ end
103
108
  end
104
109
 
105
110
  needed.map do |force_class, joining_cops|
@@ -62,7 +62,7 @@ module RuboCop
62
62
  end
63
63
 
64
64
  def begins_its_line?(range)
65
- (range.source_line =~ /\S/) == range.column
65
+ range.source_line.index(/\S/) == range.column
66
66
  end
67
67
 
68
68
  # Returns, for example, a bare `if` node if the given node is an `if`
@@ -123,6 +123,10 @@ module RuboCop
123
123
  node1.loc.line == node2.loc.line
124
124
  end
125
125
 
126
+ def indent(node)
127
+ ' ' * node.loc.column
128
+ end
129
+
126
130
  def to_supported_styles(enforced_style)
127
131
  enforced_style
128
132
  .sub(/^Enforced/, 'Supported')
@@ -10,18 +10,26 @@ module RuboCop
10
10
  end
11
11
  private_constant :ANY
12
12
 
13
- class << self
14
- attr_reader :parsed_cache
15
- end
16
- @parsed_cache = {}
17
-
18
13
  # @return [Regexp::Expression::Root, nil]
19
- def parsed_tree
14
+ # Note: we extend Regexp nodes to provide `loc` and `expression`
15
+ # see `ext/regexp_parser`.
16
+ attr_reader :parsed_tree
17
+
18
+ def assign_properties(*)
19
+ super
20
+
20
21
  str = with_interpolations_blanked
21
- Ext::RegexpNode.parsed_cache[str] ||= begin
22
- Regexp::Parser.parse(str, options: options)
22
+ begin
23
+ @parsed_tree = Regexp::Parser.parse(str, options: options)
23
24
  rescue StandardError
24
- nil
25
+ @parsed_tree = nil
26
+ else
27
+ origin = loc.begin.end
28
+ source = @parsed_tree.to_s
29
+ @parsed_tree.each_expression(true) do |e|
30
+ e.origin = origin
31
+ e.source = source
32
+ end
25
33
  end
26
34
  end
27
35
 
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Ext
5
+ # Extensions for `regexp_parser` gem
6
+ module RegexpParser
7
+ # Source map for RegexpParser nodes
8
+ class Map < ::Parser::Source::Map
9
+ attr_reader :body, :quantifier, :begin, :end
10
+
11
+ def initialize(expression, body:, quantifier: nil, begin_l: nil, end_l: nil)
12
+ @begin = begin_l
13
+ @end = end_l
14
+ @body = body
15
+ @quantifier = quantifier
16
+ super(expression)
17
+ end
18
+ end
19
+
20
+ module Expression
21
+ # Add `expression` and `loc` to all `regexp_parser` nodes
22
+ module Base
23
+ attr_accessor :origin, :source
24
+
25
+ def start_index
26
+ # ts is a byte index; convert it to a character index
27
+ @start_index ||= source.byteslice(0, ts).length
28
+ end
29
+
30
+ # Shortcut to `loc.expression`
31
+ def expression
32
+ @expression ||= begin
33
+ origin.adjust(begin_pos: start_index, end_pos: start_index + full_length)
34
+ end
35
+ end
36
+
37
+ # @returns a location map like `parser` does, with:
38
+ # - expression: complete expression
39
+ # - quantifier: for `+`, `{1,2}`, etc.
40
+ # - begin/end: for `[` and `]` (only CharacterSet for now)
41
+ #
42
+ # E.g.
43
+ # [a-z]{2,}
44
+ # ^^^^^^^^^ expression
45
+ # ^^^^ quantifier
46
+ # ^^^^^ body
47
+ # ^ begin
48
+ # ^ end
49
+ #
50
+ # Please open issue if you need other locations
51
+ def loc
52
+ @loc ||= begin
53
+ Map.new(expression, **build_location)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def build_location
60
+ return { body: expression } unless (q = quantifier)
61
+
62
+ body = expression.adjust(end_pos: -q.text.length)
63
+ q_loc = expression.with(begin_pos: body.end_pos)
64
+ { body: body, quantifier: q_loc }
65
+ end
66
+ end
67
+
68
+ # Provide `CharacterSet` with `begin` and `end` locations.
69
+ module CharacterSet
70
+ def build_location
71
+ h = super
72
+ body = h[:body]
73
+ h.merge!(
74
+ begin_l: body.with(end_pos: body.begin_pos + 1),
75
+ end_l: body.with(begin_pos: body.end_pos - 1)
76
+ )
77
+ end
78
+ end
79
+ end
80
+ ::Regexp::Expression::Base.include Expression::Base
81
+ ::Regexp::Expression::CharacterSet.include Expression::CharacterSet
82
+ end
83
+ end
84
+ end