rubocop 1.3.1 → 1.5.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +67 -11
  4. data/lib/rubocop.rb +5 -0
  5. data/lib/rubocop/cli.rb +5 -1
  6. data/lib/rubocop/cli/command/execute_runner.rb +26 -11
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +80 -0
  8. data/lib/rubocop/config_loader.rb +1 -1
  9. data/lib/rubocop/config_loader_resolver.rb +5 -1
  10. data/lib/rubocop/config_obsoletion.rb +21 -3
  11. data/lib/rubocop/config_regeneration.rb +1 -1
  12. data/lib/rubocop/config_validator.rb +8 -1
  13. data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
  14. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  15. data/lib/rubocop/cop/generator.rb +1 -1
  16. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
  17. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +80 -10
  18. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
  19. data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
  20. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
  21. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  22. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  23. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  24. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  25. data/lib/rubocop/cop/lint/missing_super.rb +7 -4
  26. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +1 -1
  27. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
  28. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +20 -6
  29. data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
  30. data/lib/rubocop/cop/metrics/block_length.rb +13 -7
  31. data/lib/rubocop/cop/metrics/method_length.rb +7 -2
  32. data/lib/rubocop/cop/metrics/parameter_lists.rb +64 -1
  33. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
  34. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
  35. data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
  36. data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -2
  37. data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
  38. data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
  39. data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
  40. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  41. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
  42. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  43. data/lib/rubocop/cop/style/and_or.rb +10 -0
  44. data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
  45. data/lib/rubocop/cop/style/documentation.rb +12 -1
  46. data/lib/rubocop/cop/style/format_string.rb +8 -3
  47. data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
  48. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  49. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
  50. data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
  51. data/lib/rubocop/cop/style/redundant_argument.rb +75 -0
  52. data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
  53. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  54. data/lib/rubocop/cop/style/sole_nested_conditional.rb +49 -3
  55. data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
  56. data/lib/rubocop/cop/util.rb +1 -1
  57. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  58. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  59. data/lib/rubocop/core_ext/hash.rb +20 -0
  60. data/lib/rubocop/ext/regexp_node.rb +5 -10
  61. data/lib/rubocop/ext/regexp_parser.rb +2 -9
  62. data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
  63. data/lib/rubocop/options.rb +5 -0
  64. data/lib/rubocop/rake_task.rb +2 -2
  65. data/lib/rubocop/runner.rb +1 -1
  66. data/lib/rubocop/version.rb +1 -1
  67. metadata +12 -10
  68. data/bin/console +0 -10
  69. data/bin/rubocop-profile +0 -32
  70. data/bin/setup +0 -7
@@ -16,7 +16,7 @@ module RuboCop
16
16
  match = generation_command.match(COMMAND_REGEX)
17
17
  return DEFAULT_OPTIONS unless match
18
18
 
19
- options = match[1].split(' ')
19
+ options = match[1].split
20
20
  Options.new.parse(options).first
21
21
  end
22
22
 
@@ -42,7 +42,7 @@ module RuboCop
42
42
  ConfigLoader.default_configuration.key?(key)
43
43
  end
44
44
 
45
- @config_obsoletion.reject_obsolete_cops_and_parameters
45
+ check_obsoletions
46
46
 
47
47
  alert_about_unrecognized_cops(invalid_cop_names)
48
48
  check_target_ruby
@@ -68,6 +68,13 @@ module RuboCop
68
68
 
69
69
  attr_reader :target_ruby
70
70
 
71
+ def check_obsoletions
72
+ @config_obsoletion.reject_obsolete_cops_and_parameters
73
+ return unless @config_obsoletion.warnings.any?
74
+
75
+ warn Rainbow("Warning: #{@config_obsoletion.warnings.join("\n")}").yellow
76
+ end
77
+
71
78
  def check_target_ruby
72
79
  return if target_ruby.supported?
73
80
 
@@ -39,16 +39,31 @@ module RuboCop
39
39
  private
40
40
 
41
41
  def disable_offense(range)
42
- eol_comment = " # rubocop:todo #{cop_name}"
43
- needed_line_length = (range.source_line + eol_comment).length
44
- if needed_line_length <= max_line_length
45
- disable_offense_at_end_of_line(range_of_first_line(range),
46
- eol_comment)
42
+ heredoc_range = surrounding_heredoc(range)
43
+ if heredoc_range
44
+ disable_offense_before_and_after(range_by_lines(heredoc_range))
47
45
  else
48
- disable_offense_before_and_after(range_by_lines(range))
46
+ eol_comment = " # rubocop:todo #{cop_name}"
47
+ needed_line_length = (range.source_line + eol_comment).length
48
+ if needed_line_length <= max_line_length
49
+ disable_offense_at_end_of_line(range_of_first_line(range), eol_comment)
50
+ else
51
+ disable_offense_before_and_after(range_by_lines(range))
52
+ end
49
53
  end
50
54
  end
51
55
 
56
+ def surrounding_heredoc(offense_range)
57
+ # The empty offense range is an edge case that can be reached from the Lint/Syntax cop.
58
+ return nil if offense_range.empty?
59
+
60
+ heredoc_nodes = processed_source.ast.each_descendant.select do |node|
61
+ node.respond_to?(:heredoc?) && node.heredoc?
62
+ end
63
+ heredoc_nodes.map { |node| node.loc.expression.join(node.loc.heredoc_end) }
64
+ .find { |range| range.contains?(offense_range) }
65
+ end
66
+
52
67
  def range_of_first_line(range)
53
68
  begin_of_first_line = range.begin_pos - range.column
54
69
  end_of_first_line = begin_of_first_line + range.source_line.length
@@ -48,7 +48,7 @@ module RuboCop
48
48
  def autocorrect_multiline_words(node, escape, delimiters)
49
49
  contents = process_multiline_words(node, escape, delimiters)
50
50
  contents << end_content(node.source)
51
- contents.join('')
51
+ contents.join
52
52
  end
53
53
 
54
54
  def autocorrect_words(node, escape, delimiters)
@@ -139,7 +139,7 @@ module RuboCop
139
139
  badge: badge,
140
140
  version_added: version_added)
141
141
 
142
- injector.inject do
142
+ injector.inject do # rubocop:disable Lint/UnexpectedBlockArity
143
143
  output.puts(format(CONFIGURATION_ADDED_MESSAGE,
144
144
  configuration_file_path: config_file_path))
145
145
  end
@@ -38,7 +38,7 @@ module RuboCop
38
38
 
39
39
  def assertions_using_described_class_msg
40
40
  described_class_msg(processed_source.ast).reject do |node|
41
- node.ancestors.any?(&method(:rspec_expectation_on_msg?))
41
+ node.ancestors.any? { |ancestor| rspec_expectation_on_msg?(ancestor) }
42
42
  end
43
43
  end
44
44
 
@@ -3,17 +3,18 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Layout
6
- # This cop checks whether method definitions are
7
- # separated by one empty line.
6
+ # This cop checks whether class/module/method definitions are
7
+ # separated by one or more empty lines.
8
8
  #
9
9
  # `NumberOfEmptyLines` can be an integer (default is 1) or
10
10
  # an array (e.g. [1, 2]) to specify a minimum and maximum
11
11
  # number of empty lines permitted.
12
12
  #
13
13
  # `AllowAdjacentOneLineDefs` configures whether adjacent
14
- # one-line method definitions are considered an offense.
14
+ # one-line definitions are considered an offense.
15
15
  #
16
- # @example
16
+ # @example EmptyLineBetweenMethodDefs: true (default)
17
+ # # checks for empty lines between method definitions.
17
18
  #
18
19
  # # bad
19
20
  # def a
@@ -29,11 +30,57 @@ module RuboCop
29
30
  #
30
31
  # def b
31
32
  # end
33
+ #
34
+ # @example EmptyLineBetweenClassDefs: true (default)
35
+ # # checks for empty lines between class definitions.
36
+ #
37
+ # # bad
38
+ # class A
39
+ # end
40
+ # class B
41
+ # end
42
+ # def b
43
+ # end
44
+ #
45
+ # @example
46
+ #
47
+ # # good
48
+ # class A
49
+ # end
50
+ #
51
+ # class B
52
+ # end
53
+ #
54
+ # def b
55
+ # end
56
+ #
57
+ # @example EmptyLineBetweenModuleDefs: true (default)
58
+ # # checks for empty lines between module definitions.
59
+ #
60
+ # # bad
61
+ # module A
62
+ # end
63
+ # module B
64
+ # end
65
+ # def b
66
+ # end
67
+ #
68
+ # @example
69
+ #
70
+ # # good
71
+ # module A
72
+ # end
73
+ #
74
+ # module B
75
+ # end
76
+ #
77
+ # def b
78
+ # end
32
79
  class EmptyLineBetweenDefs < Base
33
80
  include RangeHelp
34
81
  extend AutoCorrector
35
82
 
36
- MSG = 'Use empty lines between method definitions.'
83
+ MSG = 'Use empty lines between %<type>s definitions.'
37
84
 
38
85
  def self.autocorrect_incompatible_with
39
86
  [Layout::EmptyLines]
@@ -47,7 +94,7 @@ module RuboCop
47
94
  def on_begin(node)
48
95
  node.children.each_cons(2) do |prev, n|
49
96
  nodes = [prev, n]
50
- check_defs(nodes) if nodes.all?(&method(:def_node?))
97
+ check_defs(nodes) if nodes.all? { |def_candidate| candidate?(def_candidate) }
51
98
  end
52
99
  end
53
100
 
@@ -57,8 +104,9 @@ module RuboCop
57
104
  return if nodes.all?(&:single_line?) &&
58
105
  cop_config['AllowAdjacentOneLineDefs']
59
106
 
60
- location = nodes.last.loc.keyword.join(nodes.last.loc.name)
61
- add_offense(location) do |corrector|
107
+ correction_node = nodes.last
108
+ location = correction_node.loc.keyword.join(correction_node.loc.name)
109
+ add_offense(location, message: message(correction_node)) do |corrector|
62
110
  autocorrect(corrector, *nodes)
63
111
  end
64
112
  end
@@ -83,10 +131,32 @@ module RuboCop
83
131
 
84
132
  private
85
133
 
86
- def def_node?(node)
134
+ def candidate?(node)
87
135
  return unless node
88
136
 
89
- node.def_type? || node.defs_type?
137
+ method_candidate?(node) || class_candidate?(node) || module_candidate?(node)
138
+ end
139
+
140
+ def method_candidate?(node)
141
+ cop_config['EmptyLineBetweenMethodDefs'] && (node.def_type? || node.defs_type?)
142
+ end
143
+
144
+ def class_candidate?(node)
145
+ cop_config['EmptyLineBetweenClassDefs'] && node.class_type?
146
+ end
147
+
148
+ def module_candidate?(node)
149
+ cop_config['EmptyLineBetweenModuleDefs'] && node.module_type?
150
+ end
151
+
152
+ def message(node)
153
+ type = case node.type
154
+ when :def, :defs
155
+ :method
156
+ else
157
+ node.type
158
+ end
159
+ format(MSG, type: type)
90
160
  end
91
161
 
92
162
  def multiple_blank_lines_groups?(first_def_node, second_def_node)
@@ -45,7 +45,8 @@ module RuboCop
45
45
  MSG = 'Empty line detected around arguments.'
46
46
 
47
47
  def on_send(node)
48
- return if node.single_line? || node.arguments.empty?
48
+ return if node.single_line? || node.arguments.empty? ||
49
+ receiver_and_method_call_on_different_lines?(node)
49
50
 
50
51
  extra_lines(node) do |range|
51
52
  add_offense(range) do |corrector|
@@ -57,6 +58,10 @@ module RuboCop
57
58
 
58
59
  private
59
60
 
61
+ def receiver_and_method_call_on_different_lines?(node)
62
+ node.receiver && node.receiver.loc.last_line != node.loc.selector&.line
63
+ end
64
+
60
65
  def empty_lines(node)
61
66
  lines = processed_lines(node)
62
67
  lines.select! { |code, _| code.empty? }
@@ -37,14 +37,14 @@ module RuboCop
37
37
  # # good
38
38
  # puts 'Hello' # Return character is CR+LF on all platfoms.
39
39
  #
40
- class EndOfLine < Cop
40
+ class EndOfLine < Base
41
41
  include ConfigurableEnforcedStyle
42
42
  include RangeHelp
43
43
 
44
44
  MSG_DETECTED = 'Carriage return character detected.'
45
45
  MSG_MISSING = 'Carriage return character missing.'
46
46
 
47
- def investigate(processed_source)
47
+ def on_new_investigation
48
48
  last_line = last_line(processed_source)
49
49
 
50
50
  processed_source.raw_source.each_line.with_index do |line, index|
@@ -54,9 +54,9 @@ module RuboCop
54
54
  next unless msg
55
55
  next if unimportant_missing_cr?(index, last_line, line)
56
56
 
57
- range =
58
- source_range(processed_source.buffer, index + 1, 0, line.length)
59
- add_offense(nil, location: range, message: msg)
57
+ range = source_range(processed_source.buffer, index + 1, 0, line.length)
58
+
59
+ add_offense(range, message: msg)
60
60
  # Usually there will be carriage return characters on all or none
61
61
  # of the lines in a file, so we report only one offense.
62
62
  break
@@ -207,8 +207,13 @@ module RuboCop
207
207
  PATTERN
208
208
 
209
209
  def base_range(send_node, arg_node)
210
- range_between(send_node.source_range.begin_pos,
211
- arg_node.source_range.begin_pos)
210
+ parent = send_node.parent
211
+ start_node = if parent && (parent.splat_type? || parent.kwsplat_type?)
212
+ send_node.parent
213
+ else
214
+ send_node
215
+ end
216
+ range_between(start_node.source_range.begin_pos, arg_node.source_range.begin_pos)
212
217
  end
213
218
 
214
219
  # Returns the column of the given range. For single line ranges, this
@@ -51,7 +51,7 @@ module RuboCop
51
51
  alias on_csend on_send
52
52
 
53
53
  def on_const(node)
54
- return unless node.loc.double_colon
54
+ return unless node.loc.respond_to?(:double_colon) && node.loc.double_colon
55
55
 
56
56
  check_space_after_double_colon(node)
57
57
  end
@@ -53,7 +53,8 @@ module RuboCop
53
53
  def find_offense_node(node, regexp_receiver)
54
54
  return node unless node.parent
55
55
 
56
- if node.parent.send_type? || method_chain_to_regexp_receiver?(node, regexp_receiver)
56
+ if (node.parent.send_type? && node.receiver) ||
57
+ method_chain_to_regexp_receiver?(node, regexp_receiver)
57
58
  node = find_offense_node(node.parent, regexp_receiver)
58
59
  end
59
60
 
@@ -23,10 +23,11 @@ module RuboCop
23
23
  'Use double quoted strings if you need interpolation.'
24
24
 
25
25
  def on_str(node)
26
- parent = node.parent
27
- return if parent && (parent.dstr_type? || parent.regexp_type?)
26
+ return unless node
27
+ return if string_or_regex?(node.parent)
28
28
  return unless /(?<!\\)#\{.*\}/.match?(node.source)
29
29
  return if heredoc?(node)
30
+ return unless node.loc.begin && node.loc.end
30
31
 
31
32
  add_offense(node) do |corrector|
32
33
  autocorrect(corrector, node)
@@ -35,6 +36,10 @@ module RuboCop
35
36
 
36
37
  private
37
38
 
39
+ def string_or_regex?(node)
40
+ node&.dstr_type? || node&.regexp_type?
41
+ end
42
+
38
43
  def autocorrect(corrector, node)
39
44
  starting_token, ending_token = if node.source.include?('"')
40
45
  ['%{', '}']
@@ -95,7 +95,7 @@ module RuboCop
95
95
  def autocorrected_value_for_array(node)
96
96
  return node.source.gsub('"', '\"') unless node.percent_literal?
97
97
 
98
- contents_range(node).source.split(' ').to_s.gsub('"', '\"')
98
+ contents_range(node).source.split.to_s.gsub('"', '\"')
99
99
  end
100
100
 
101
101
  # Does node print its own source when converted to a string?
@@ -6,6 +6,11 @@ module RuboCop
6
6
  # This cop checks for the presence of constructors and lifecycle callbacks
7
7
  # without calls to `super`.
8
8
  #
9
+ # This cop does not consider `method_missing` (and `respond_to_missing?`)
10
+ # because in some cases it makes sense to overtake what is considered a
11
+ # missing method. In other cases, the theoretical ideal handling could be
12
+ # challenging or verbose for no actual gain.
13
+ #
9
14
  # @example
10
15
  # # bad
11
16
  # class Employee < Person
@@ -43,15 +48,13 @@ module RuboCop
43
48
 
44
49
  STATELESS_CLASSES = %w[BasicObject Object].freeze
45
50
 
46
- OBJECT_LIFECYCLE_CALLBACKS = %i[method_missing respond_to_missing?].freeze
47
51
  CLASS_LIFECYCLE_CALLBACKS = %i[inherited].freeze
48
52
  METHOD_LIFECYCLE_CALLBACKS = %i[method_added method_removed method_undefined
49
53
  singleton_method_added singleton_method_removed
50
54
  singleton_method_undefined].freeze
51
55
 
52
- CALLBACKS = (OBJECT_LIFECYCLE_CALLBACKS +
53
- CLASS_LIFECYCLE_CALLBACKS +
54
- METHOD_LIFECYCLE_CALLBACKS).to_set.freeze
56
+ CALLBACKS = (CLASS_LIFECYCLE_CALLBACKS +
57
+ METHOD_LIFECYCLE_CALLBACKS).to_set.freeze
55
58
 
56
59
  def on_def(node)
57
60
  return unless offender?(node)
@@ -40,7 +40,7 @@ module RuboCop
40
40
  # do_something
41
41
  # end
42
42
  #
43
- class NoReturnInBeginEndBlocks < Cop
43
+ class NoReturnInBeginEndBlocks < Base
44
44
  MSG = 'Do not `return` in `begin..end` blocks in assignment contexts.'
45
45
 
46
46
  def on_lvasgn(node)
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for a block that is known to need more positional
7
+ # block arguments than are given (by default this is configured for
8
+ # `Enumerable` methods needing 2 arguments). Optional arguments are allowed,
9
+ # although they don't generally make sense as the default value will
10
+ # be used. Blocks that have no receiver, or take splatted arguments
11
+ # (ie. `*args`) are always accepted.
12
+ #
13
+ # Keyword arguments (including `**kwargs`) do not get counted towards
14
+ # this, as they are not used by the methods in question.
15
+ #
16
+ # NOTE: This cop matches for method names only and hence cannot tell apart
17
+ # methods with same name in different classes.
18
+ #
19
+ # Method names and their expected arity can be configured like this:
20
+ #
21
+ # Methods:
22
+ # inject: 2
23
+ # reduce: 2
24
+ #
25
+ # @example
26
+ # # bad
27
+ # values.reduce {}
28
+ # values.min { |a| a }
29
+ # values.sort { |a; b| a + b }
30
+ #
31
+ # # good
32
+ # values.reduce { |memo, obj| memo << obj }
33
+ # values.min { |a, b| a <=> b }
34
+ # values.sort { |*x| x[0] <=> x[1] }
35
+ #
36
+ class UnexpectedBlockArity < Base
37
+ MSG = '`%<method>s` expects at least %<expected>i positional arguments, got %<actual>i.'
38
+
39
+ def on_block(node)
40
+ return if acceptable?(node)
41
+
42
+ expected = expected_arity(node.method_name)
43
+ actual = arg_count(node)
44
+ return if actual >= expected
45
+
46
+ message = format(MSG, method: node.method_name, expected: expected, actual: actual)
47
+ add_offense(node, message: message)
48
+ end
49
+
50
+ alias on_numblock on_block
51
+
52
+ private
53
+
54
+ def methods
55
+ cop_config.fetch('Methods', [])
56
+ end
57
+
58
+ def acceptable?(node)
59
+ !(included_method?(node.method_name) && node.receiver)
60
+ end
61
+
62
+ def included_method?(name)
63
+ methods.key?(name.to_s)
64
+ end
65
+
66
+ def expected_arity(method)
67
+ cop_config['Methods'][method.to_s]
68
+ end
69
+
70
+ def arg_count(node)
71
+ return node.children[1] if node.numblock_type? # the maximum numbered param for the block
72
+
73
+ # Only `arg`, `optarg` and `mlhs` (destructuring) count as arguments that
74
+ # can be used. Keyword arguments are not used for these methods so are
75
+ # ignored.
76
+ node.arguments.count do |arg|
77
+ return Float::INFINITY if arg.restarg_type?
78
+
79
+ arg.arg_type? || arg.optarg_type? || arg.mlhs_type?
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end