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
@@ -7,42 +7,102 @@ module RuboCop
7
7
  # configured style, snake_case, normalcase, or non_integer,
8
8
  # for their numbering.
9
9
  #
10
+ # Additionally, `CheckMethodNames` and `CheckSymbols` configuration options
11
+ # can be used to specify whether method names and symbols should be checked.
12
+ # Both are enabled by default.
13
+ #
10
14
  # @example EnforcedStyle: snake_case
11
15
  # # bad
12
- #
16
+ # :some_sym1
13
17
  # variable1 = 1
14
18
  #
15
- # # good
19
+ # def some_method1; end
16
20
  #
21
+ # def some_method_1(arg1); end
22
+ #
23
+ # # good
24
+ # :some_sym_1
17
25
  # variable_1 = 1
18
26
  #
27
+ # def some_method_1; end
28
+ #
29
+ # def some_method_1(arg_1); end
30
+ #
19
31
  # @example EnforcedStyle: normalcase (default)
20
32
  # # bad
21
- #
33
+ # :some_sym_1
22
34
  # variable_1 = 1
23
35
  #
24
- # # good
36
+ # def some_method_1; end
25
37
  #
38
+ # def some_method1(arg_1); end
39
+ #
40
+ # # good
41
+ # :some_sym1
26
42
  # variable1 = 1
27
43
  #
44
+ # def some_method1; end
45
+ #
46
+ # def some_method1(arg1); end
47
+ #
28
48
  # @example EnforcedStyle: non_integer
29
49
  # # bad
50
+ # :some_sym1
51
+ # :some_sym_1
30
52
  #
31
53
  # variable1 = 1
32
- #
33
54
  # variable_1 = 1
34
55
  #
56
+ # def some_method1; end
57
+ #
58
+ # def some_method_1; end
59
+ #
60
+ # def some_methodone(arg1); end
61
+ # def some_methodone(arg_1); end
62
+ #
35
63
  # # good
64
+ # :some_symone
65
+ # :some_sym_one
36
66
  #
37
67
  # variableone = 1
38
- #
39
68
  # variable_one = 1
69
+ #
70
+ # def some_methodone; end
71
+ #
72
+ # def some_method_one; end
73
+ #
74
+ # def some_methodone(argone); end
75
+ # def some_methodone(arg_one); end
76
+ #
77
+ # # In the following examples, we assume `EnforcedStyle: normalcase` (default).
78
+ #
79
+ # @example CheckMethodNames: true (default)
80
+ # # bad
81
+ # def some_method_1; end
82
+ #
83
+ # @example CheckMethodNames: false
84
+ # # good
85
+ # def some_method_1; end
86
+ #
87
+ # @example CheckSymbols: true (default)
88
+ # # bad
89
+ # :some_sym_1
90
+ #
91
+ # @example CheckSymbols: false
92
+ # # good
93
+ # :some_sym_1
94
+ #
95
+ # @example AllowedIdentifier: [capture3]
96
+ # # good
97
+ # expect(Open3).to receive(:capture3)
98
+ #
40
99
  class VariableNumber < Base
41
100
  include ConfigurableNumbering
42
101
 
43
- MSG = 'Use %<style>s for variable numbers.'
102
+ MSG = 'Use %<style>s for %<identifier_type>s numbers.'
44
103
 
45
104
  def on_arg(node)
105
+ @node = node
46
106
  name, = *node
47
107
  check_name(node, name, node.loc.name)
48
108
  end
@@ -50,10 +110,40 @@ module RuboCop
50
110
  alias on_ivasgn on_arg
51
111
  alias on_cvasgn on_arg
52
112
 
113
+ def on_def(node)
114
+ @node = node
115
+ return if allowed_identifier?(node.method_name)
116
+
117
+ check_name(node, node.method_name, node.loc.name) if cop_config['CheckMethodNames']
118
+ end
119
+ alias on_defs on_def
120
+
121
+ def on_sym(node)
122
+ @node = node
123
+ return if allowed_identifier?(node.value)
124
+
125
+ check_name(node, node.value, node) if cop_config['CheckSymbols']
126
+ end
127
+
53
128
  private
54
129
 
55
130
  def message(style)
56
- format(MSG, style: style)
131
+ identifier_type =
132
+ case @node.type
133
+ when :def, :defs then 'method name'
134
+ when :sym then 'symbol'
135
+ else 'variable'
136
+ end
137
+
138
+ format(MSG, style: style, identifier_type: identifier_type)
139
+ end
140
+
141
+ def allowed_identifier?(name)
142
+ allowed_identifiers.include?(name.to_s)
143
+ end
144
+
145
+ def allowed_identifiers
146
+ cop_config.fetch('AllowedIdentifiers', [])
57
147
  end
58
148
  end
59
149
  end
@@ -66,9 +66,7 @@ module RuboCop
66
66
  node.each_child_node do |expr|
67
67
  if expr.send_type?
68
68
  correct_send(expr, corrector)
69
- elsif expr.return_type?
70
- correct_other(expr, corrector)
71
- elsif expr.assignment?
69
+ elsif expr.return_type? || expr.assignment?
72
70
  correct_other(expr, corrector)
73
71
  end
74
72
  end
@@ -131,10 +131,6 @@ module RuboCop
131
131
  "#{indent(macro)}#{macro.method_name} #{rest_args.map(&:source).join(', ')}"
132
132
  end
133
133
  end
134
-
135
- def indent(node)
136
- ' ' * node.loc.column
137
- end
138
134
  end
139
135
  end
140
136
  end
@@ -227,10 +227,6 @@ module RuboCop
227
227
  range_between(node.parent.loc.keyword.begin_pos, node.loc.expression.end_pos)
228
228
  end
229
229
 
230
- def indent(node)
231
- ' ' * node.loc.column
232
- end
233
-
234
230
  # Named captures work with `=~` (if regexp is on lhs) and with `match` (both sides)
235
231
  def regexp_with_working_captures?(node)
236
232
  case node.type
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for places where custom logic on rejection nils from arrays
7
+ # and hashes can be replaced with `{Array,Hash}#{compact,compact!}`.
8
+ #
9
+ # It is marked as unsafe by default because false positives may occur in the
10
+ # nil check of block arguments to the receiver object.
11
+ # For example, `[[1, 2], [3, nil]].reject { |first, second| second.nil? }`
12
+ # and `[[1, 2], [3, nil]].compact` are not compatible. This will work fine
13
+ # when the receiver is a hash object.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # array.reject { |e| e.nil? }
18
+ # array.select { |e| !e.nil? }
19
+ #
20
+ # # good
21
+ # array.compact
22
+ #
23
+ # # bad
24
+ # hash.reject! { |k, v| v.nil? }
25
+ # hash.select! { |k, v| !v.nil? }
26
+ #
27
+ # # good
28
+ # hash.compact!
29
+ #
30
+ class CollectionCompact < Base
31
+ include RangeHelp
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Use `%<good>s` instead of `%<bad>s`.'
35
+
36
+ RESTRICT_ON_SEND = %i[reject reject! select select!].freeze
37
+
38
+ def_node_matcher :reject_method?, <<~PATTERN
39
+ (block
40
+ (send
41
+ _ ${:reject :reject!})
42
+ $(args ...)
43
+ (send
44
+ $(lvar _) :nil?))
45
+ PATTERN
46
+
47
+ def_node_matcher :select_method?, <<~PATTERN
48
+ (block
49
+ (send
50
+ _ ${:select :select!})
51
+ $(args ...)
52
+ (send
53
+ (send
54
+ $(lvar _) :nil?) :!))
55
+ PATTERN
56
+
57
+ def on_send(node)
58
+ block_node = node.parent
59
+ return unless block_node&.block_type?
60
+
61
+ return unless (method_name, args, receiver =
62
+ reject_method?(block_node) || select_method?(block_node))
63
+
64
+ return unless args.last.source == receiver.source
65
+
66
+ range = offense_range(node, block_node)
67
+ good = good_method_name(method_name)
68
+ message = format(MSG, good: good, bad: range.source)
69
+
70
+ add_offense(range, message: message) do |corrector|
71
+ corrector.replace(range, good)
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def good_method_name(method_name)
78
+ if method_name.to_s.end_with?('!')
79
+ 'compact!'
80
+ else
81
+ 'compact'
82
+ end
83
+ end
84
+
85
+ def offense_range(send_node, block_node)
86
+ range_between(send_node.loc.selector.begin_pos, block_node.loc.end.end_pos)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -25,7 +25,7 @@ module RuboCop
25
25
  # end
26
26
  # end
27
27
  #
28
- # # good
28
+ # # good, inline comments in heredoc
29
29
  # UNSAFE_STRING_METHODS.each do |unsafe_method|
30
30
  # if 'String'.respond_to?(unsafe_method)
31
31
  # class_eval <<-EOT, __FILE__, __LINE__ + 1
@@ -41,25 +41,127 @@ module RuboCop
41
41
  # end
42
42
  # end
43
43
  #
44
+ # # good, block comments in heredoc
45
+ # class_eval <<-EOT, __FILE__, __LINE__ + 1
46
+ # # def capitalize!(*params)
47
+ # # @dirty = true
48
+ # # super
49
+ # # end
50
+ #
51
+ # def #{unsafe_method}!(*params)
52
+ # @dirty = true
53
+ # super
54
+ # end
55
+ # EOT
56
+ #
57
+ # # good, block comments before heredoc
58
+ # class_eval(
59
+ # # def capitalize!(*params)
60
+ # # @dirty = true
61
+ # # super
62
+ # # end
63
+ #
64
+ # <<-EOT, __FILE__, __LINE__ + 1
65
+ # def #{unsafe_method}!(*params)
66
+ # @dirty = true
67
+ # super
68
+ # end
69
+ # EOT
70
+ # )
71
+ #
72
+ # # bad - interpolated string without comment
73
+ # class_eval("def #{unsafe_method}!(*params); end")
74
+ #
75
+ # # good - with inline comment or replace it with block comment using heredoc
76
+ # class_eval("def #{unsafe_method}!(*params); end # def capitalize!(*params); end")
44
77
  class DocumentDynamicEvalDefinition < Base
78
+ BLOCK_COMMENT_REGEXP = /^\s*#(?!{)/.freeze
79
+ COMMENT_REGEXP = /\s*#(?!{).*/.freeze
45
80
  MSG = 'Add a comment block showing its appearance if interpolated.'
46
81
 
47
82
  RESTRICT_ON_SEND = %i[eval class_eval module_eval instance_eval].freeze
48
83
 
49
84
  def on_send(node)
50
85
  arg_node = node.first_argument
51
- return unless arg_node&.dstr_type?
52
86
 
53
- add_offense(node.loc.selector) unless comment_docs?(arg_node)
87
+ return unless arg_node&.dstr_type? && interpolated?(arg_node)
88
+ return if inline_comment_docs?(arg_node) ||
89
+ arg_node.heredoc? && comment_block_docs?(arg_node)
90
+
91
+ add_offense(node.loc.selector)
54
92
  end
55
93
 
56
94
  private
57
95
 
58
- def comment_docs?(node)
96
+ def interpolated?(arg_node)
97
+ arg_node.each_child_node(:begin).any?
98
+ end
99
+
100
+ def inline_comment_docs?(node)
59
101
  node.each_child_node(:begin).all? do |begin_node|
60
102
  source_line = processed_source.lines[begin_node.first_line - 1]
61
- source_line.match?(/\s*#[^{]+/)
103
+ source_line.match?(COMMENT_REGEXP)
104
+ end
105
+ end
106
+
107
+ def comment_block_docs?(arg_node)
108
+ comments = heredoc_comment_blocks(arg_node.loc.heredoc_body.line_span)
109
+ .concat(preceding_comment_blocks(arg_node.parent))
110
+
111
+ return if comments.none?
112
+
113
+ regexp = comment_regexp(arg_node)
114
+ comments.any? { |comment| regexp.match?(comment) } || regexp.match?(comments.join)
115
+ end
116
+
117
+ def preceding_comment_blocks(node)
118
+ # Collect comments in the method call, but outside the heredoc
119
+ comments = processed_source.each_comment_in_lines(node.loc.expression.line_span)
120
+
121
+ comments.each_with_object({}) do |comment, hash|
122
+ merge_adjacent_comments(comment.text, comment.loc.line, hash)
123
+ end.values
124
+ end
125
+
126
+ def heredoc_comment_blocks(heredoc_body)
127
+ # Collect comments inside the heredoc
128
+ line_range = (heredoc_body.begin - 1)..(heredoc_body.end - 1)
129
+ lines = processed_source.lines[line_range]
130
+
131
+ lines.each_with_object({}).with_index(line_range.begin) do |(line, hash), index|
132
+ merge_adjacent_comments(line, index, hash)
133
+ end.values
134
+ end
135
+
136
+ def merge_adjacent_comments(line, index, hash)
137
+ # Combine adjacent comment lines into a single string
138
+ return unless (line = line.dup.gsub!(BLOCK_COMMENT_REGEXP, ''))
139
+
140
+ hash[index] = if hash.keys.last == index - 1
141
+ [hash.delete(index - 1), line].join("\n")
142
+ else
143
+ line
144
+ end
145
+ end
146
+
147
+ def comment_regexp(arg_node)
148
+ # Replace the interpolations with wildcards
149
+ regexp_parts = arg_node.child_nodes.map do |n|
150
+ n.begin_type? ? /.+/ : source_to_regexp(n.source)
62
151
  end
152
+
153
+ Regexp.new(regexp_parts.join)
154
+ end
155
+
156
+ def source_to_regexp(source)
157
+ # Get the source in the heredoc being `eval`ed, without any comments
158
+ # and turn it into a regexp
159
+ return /\s+/ if source.blank?
160
+
161
+ source = source.gsub(COMMENT_REGEXP, '')
162
+ return if source.blank?
163
+
164
+ /\s*#{Regexp.escape(source.strip)}/
63
165
  end
64
166
  end
65
167
  end
@@ -55,6 +55,11 @@ module RuboCop
55
55
  # Public = Class.new
56
56
  # end
57
57
  #
58
+ # # Macro calls
59
+ # module Namespace
60
+ # extend Foo
61
+ # end
62
+ #
58
63
  class Documentation < Base
59
64
  include DocumentationComment
60
65
 
@@ -83,15 +88,21 @@ module RuboCop
83
88
  return if documentation_comment?(node) || nodoc_comment?(node)
84
89
  return if compact_namespace?(node) &&
85
90
  nodoc_comment?(outer_module(node).first)
91
+ return if macro_only?(body)
86
92
 
87
93
  add_offense(node.loc.keyword, message: format(MSG, type: type))
88
94
  end
89
95
 
96
+ def macro_only?(body)
97
+ body.respond_to?(:macro?) && body.macro? ||
98
+ body.respond_to?(:children) && body.children&.all? { |child| macro_only?(child) }
99
+ end
100
+
90
101
  def namespace?(node)
91
102
  return false unless node
92
103
 
93
104
  if node.begin_type?
94
- node.children.all?(&method(:constant_declaration?))
105
+ node.children.all? { |child| constant_declaration?(child) }
95
106
  else
96
107
  constant_definition?(node)
97
108
  end