rubocop 0.65.0 → 0.66.0

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +53 -4
  4. data/lib/rubocop.rb +4 -5
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/config.rb +1 -1
  7. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +3 -5
  8. data/lib/rubocop/cop/layout/class_structure.rb +59 -28
  9. data/lib/rubocop/cop/layout/extra_spacing.rb +18 -0
  10. data/lib/rubocop/cop/layout/indentation_width.rb +25 -5
  11. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +33 -17
  12. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +30 -11
  13. data/lib/rubocop/cop/lint/else_layout.rb +1 -0
  14. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  15. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +38 -0
  16. data/lib/rubocop/cop/lint/shadowed_exception.rb +14 -1
  17. data/lib/rubocop/cop/lint/to_json.rb +38 -0
  18. data/lib/rubocop/cop/lint/void.rb +1 -1
  19. data/lib/rubocop/cop/message_annotator.rb +4 -4
  20. data/lib/rubocop/cop/metrics/abc_size.rb +1 -0
  21. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +31 -9
  22. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  23. data/lib/rubocop/cop/mixin/integer_node.rb +1 -1
  24. data/lib/rubocop/cop/mixin/method_preference.rb +2 -1
  25. data/lib/rubocop/cop/naming/constant_name.rb +6 -1
  26. data/lib/rubocop/cop/naming/predicate_name.rb +6 -0
  27. data/lib/rubocop/cop/rails/link_to_blank.rb +1 -1
  28. data/lib/rubocop/cop/rails/output.rb +18 -1
  29. data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
  30. data/lib/rubocop/cop/rails/time_zone.rb +10 -0
  31. data/lib/rubocop/cop/rails/validation.rb +3 -2
  32. data/lib/rubocop/cop/style/block_delimiters.rb +30 -1
  33. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  34. data/lib/rubocop/cop/style/constant_visibility.rb +66 -0
  35. data/lib/rubocop/cop/style/identical_conditional_branches.rb +8 -12
  36. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -4
  37. data/lib/rubocop/cop/style/numeric_literals.rb +16 -7
  38. data/lib/rubocop/cop/style/option_hash.rb +5 -0
  39. data/lib/rubocop/cop/style/redundant_freeze.rb +13 -1
  40. data/lib/rubocop/cop/style/redundant_self.rb +3 -1
  41. data/lib/rubocop/cop/style/stderr_puts.rb +1 -1
  42. data/lib/rubocop/cop/style/symbol_array.rb +9 -1
  43. data/lib/rubocop/cop/style/trivial_accessors.rb +5 -0
  44. data/lib/rubocop/cop/style/word_array.rb +0 -4
  45. data/lib/rubocop/cop/util.rb +4 -0
  46. data/lib/rubocop/core_ext/string.rb +47 -0
  47. data/lib/rubocop/node_pattern.rb +76 -55
  48. data/lib/rubocop/processed_source.rb +2 -2
  49. data/lib/rubocop/result_cache.rb +4 -4
  50. data/lib/rubocop/rspec/cop_helper.rb +5 -0
  51. data/lib/rubocop/rspec/expect_offense.rb +5 -5
  52. data/lib/rubocop/runner.rb +6 -13
  53. data/lib/rubocop/version.rb +1 -1
  54. metadata +15 -19
@@ -36,11 +36,19 @@ module RuboCop
36
36
  check_each_arg(arguments)
37
37
  end
38
38
 
39
- def autocorrect(range)
39
+ # @param target [RuboCop::AST::Node,Parser::Source::Range]
40
+ def autocorrect(target)
40
41
  lambda do |corrector|
41
- case range.source
42
- when /^\s+$/ then corrector.remove(range)
43
- else corrector.insert_after(range, ' ')
42
+ if target.is_a?(RuboCop::AST::Node)
43
+ if target.parent.children.first == target
44
+ corrector.insert_before(target.source_range, ' ')
45
+ else
46
+ corrector.insert_after(target.source_range, ' ')
47
+ end
48
+ elsif target.source =~ /^\s+$/
49
+ corrector.remove(target)
50
+ else
51
+ corrector.insert_after(target, ' ')
44
52
  end
45
53
  end
46
54
  end
@@ -97,11 +105,12 @@ module RuboCop
97
105
  end
98
106
 
99
107
  def check_opening_pipe_space(args, opening_pipe)
100
- first = args.first.source_range
108
+ first_arg = args.first
109
+ range = first_arg.source_range
101
110
 
102
- check_space(opening_pipe.end_pos, first.begin_pos, first,
103
- 'before first block parameter')
104
- check_no_space(opening_pipe.end_pos, first.begin_pos - 1,
111
+ check_space(opening_pipe.end_pos, range.begin_pos, range,
112
+ 'before first block parameter', first_arg)
113
+ check_no_space(opening_pipe.end_pos, range.begin_pos - 1,
105
114
  'Extra space before first')
106
115
  end
107
116
 
@@ -120,20 +129,27 @@ module RuboCop
120
129
  end
121
130
 
122
131
  def check_each_arg(args)
123
- args.children.butfirst.each do |arg|
124
- expr = arg.source_range
125
- check_no_space(
126
- range_with_surrounding_space(range: expr, side: :left).begin_pos,
127
- expr.begin_pos - 1,
128
- 'Extra space before'
129
- )
132
+ args.children.each do |arg|
133
+ check_arg(arg)
130
134
  end
131
135
  end
132
136
 
133
- def check_space(space_begin_pos, space_end_pos, range, msg)
137
+ def check_arg(arg)
138
+ arg.children.each { |a| check_arg(a) } if arg.mlhs_type?
139
+
140
+ expr = arg.source_range
141
+ check_no_space(
142
+ range_with_surrounding_space(range: expr, side: :left).begin_pos,
143
+ expr.begin_pos - 1,
144
+ 'Extra space before'
145
+ )
146
+ end
147
+
148
+ def check_space(space_begin_pos, space_end_pos, range, msg, node = nil)
134
149
  return if space_begin_pos != space_end_pos
135
150
 
136
- add_offense(range, location: range, message: "Space #{msg} missing.")
151
+ target = node || range
152
+ add_offense(target, location: range, message: "Space #{msg} missing.")
137
153
  end
138
154
 
139
155
  def check_no_space(space_begin_pos, space_end_pos, msg)
@@ -10,12 +10,16 @@ module RuboCop
10
10
  # # bad
11
11
  #
12
12
  # File.exists?(some_path)
13
+ # Dir.exists?(some_path)
14
+ # iterator?
13
15
  #
14
16
  # @example
15
17
  #
16
18
  # # good
17
19
  #
18
20
  # File.exist?(some_path)
21
+ # Dir.exist?(some_path)
22
+ # block_given?
19
23
  class DeprecatedClassMethods < Cop
20
24
  # Inner class to DeprecatedClassMethods.
21
25
  # This class exists to add abstraction and clean naming to the
@@ -25,24 +29,35 @@ module RuboCop
25
29
 
26
30
  attr_reader :class_constant, :deprecated_method, :replacement_method
27
31
 
28
- def initialize(class_constant, deprecated_method, replacement_method)
32
+ def initialize(deprecated:, replacement:, class_constant: nil)
33
+ @deprecated_method = deprecated
34
+ @replacement_method = replacement
29
35
  @class_constant = class_constant
30
- @deprecated_method = deprecated_method
31
- @replacement_method = replacement_method
32
36
  end
33
37
 
34
38
  def class_nodes
35
- @class_nodes ||= [
36
- s(:const, nil, class_constant),
37
- s(:const, s(:cbase), class_constant)
38
- ]
39
+ @class_nodes ||=
40
+ if class_constant
41
+ [
42
+ s(:const, nil, class_constant),
43
+ s(:const, s(:cbase), class_constant)
44
+ ]
45
+ else
46
+ [nil]
47
+ end
39
48
  end
40
49
  end
41
50
 
42
51
  MSG = '`%<current>s` is deprecated in favor of `%<prefer>s`.'.freeze
43
52
  DEPRECATED_METHODS_OBJECT = [
44
- DeprecatedClassMethod.new(:File, :exists?, :exist?),
45
- DeprecatedClassMethod.new(:Dir, :exists?, :exist?)
53
+ DeprecatedClassMethod.new(deprecated: :exists?,
54
+ replacement: :exist?,
55
+ class_constant: :File),
56
+ DeprecatedClassMethod.new(deprecated: :exists?,
57
+ replacement: :exist?,
58
+ class_constant: :Dir),
59
+ DeprecatedClassMethod.new(deprecated: :iterator?,
60
+ replacement: :block_given?)
46
61
  ].freeze
47
62
 
48
63
  def on_send(node)
@@ -83,8 +98,12 @@ module RuboCop
83
98
  end
84
99
 
85
100
  def method_call(class_constant, method)
86
- format('%<constant>s.%<method>s', constant: class_constant,
87
- method: method)
101
+ if class_constant
102
+ format('%<constant>s.%<method>s', constant: class_constant,
103
+ method: method)
104
+ else
105
+ format('%<method>s', method: method)
106
+ end
88
107
  end
89
108
  end
90
109
  end
@@ -55,6 +55,7 @@ module RuboCop
55
55
 
56
56
  first_else = else_branch.children.first
57
57
 
58
+ return unless first_else
58
59
  return unless first_else.source_range.line == node.loc.else.line
59
60
 
60
61
  add_offense(first_else)
@@ -87,7 +87,7 @@ module RuboCop
87
87
  def splat_args?(node)
88
88
  return false if percent?(node)
89
89
 
90
- node.arguments.butfirst.any?(&:splat_type?)
90
+ node.arguments.drop(1).any?(&:splat_type?)
91
91
  end
92
92
 
93
93
  def heredoc?(node)
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks to make sure safe navigation isn't used with `empty?` in
7
+ # a conditional.
8
+ #
9
+ # While the safe navigation operator is generally a good idea, when
10
+ # checking `foo&.empty?` in a conditional, `foo` being `nil` will actually
11
+ # do the opposite of what the author intends.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # return if foo&.empty?
16
+ # return unless foo&.empty?
17
+ #
18
+ # # good
19
+ # return if foo && foo.empty?
20
+ # return unless foo && foo.empty?
21
+ #
22
+ class SafeNavigationWithEmpty < Cop
23
+ MSG = 'Avoid calling `empty?` with the safe navigation operator ' \
24
+ 'in conditionals.'.freeze
25
+
26
+ def_node_matcher :safe_navigation_empty_in_conditional?, <<-PATTERN
27
+ (if (csend (send ...) :empty?) ...)
28
+ PATTERN
29
+
30
+ def on_if(node)
31
+ return unless safe_navigation_empty_in_conditional?(node)
32
+
33
+ add_offense(node)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -107,12 +107,25 @@ module RuboCop
107
107
  error && error.ancestors[1] == SystemCallError
108
108
  end
109
109
 
110
+ def silence_warnings
111
+ # Replaces Kernel::silence_warnings since it hides any warnings,
112
+ # including the RuboCop ones
113
+ old_verbose = $VERBOSE
114
+ $VERBOSE = nil
115
+ yield
116
+ ensure
117
+ $VERBOSE = old_verbose
118
+ end
119
+
110
120
  def evaluate_exceptions(rescue_group)
111
121
  if rescue_group
112
122
  rescued_exceptions = rescued_exceptions(rescue_group)
113
123
  rescued_exceptions.each_with_object([]) do |exception, converted|
114
124
  begin
115
- converted << Kernel.const_get(exception)
125
+ silence_warnings do
126
+ # Avoid printing deprecation warnings about constants
127
+ converted << Kernel.const_get(exception)
128
+ end
116
129
  rescue NameError
117
130
  converted << nil
118
131
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks to make sure `#to_json` includes an optional argument.
7
+ # When overriding `#to_json`, callers may invoke JSON
8
+ # generation via `JSON.generate(your_obj)`. Since `JSON#generate` allows
9
+ # for an optional argument, your method should too.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # def to_json
14
+ # end
15
+ #
16
+ # # good
17
+ # def to_json(_opts)
18
+ # end
19
+ #
20
+ class ToJSON < Cop
21
+ MSG = ' `#to_json` requires an optional argument to be parsable ' \
22
+ 'via JSON.generate(obj).'.freeze
23
+
24
+ def on_def(node)
25
+ return unless node.method?(:to_json) && node.arguments.empty?
26
+
27
+ add_offense(node)
28
+ end
29
+
30
+ def autocorrect(node)
31
+ lambda do |corrector|
32
+ corrector.insert_after(node.loc.name, '(_opts)')
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -96,7 +96,7 @@ module RuboCop
96
96
 
97
97
  def check_begin(node)
98
98
  expressions = *node
99
- expressions = expressions.drop_last(1) unless in_void_context?(node)
99
+ expressions.pop unless in_void_context?(node)
100
100
  expressions.each do |expr|
101
101
  check_expression(expr)
102
102
  end
@@ -64,7 +64,7 @@ module RuboCop
64
64
  end
65
65
 
66
66
  def urls
67
- [style_guide_url, reference_url].compact
67
+ [style_guide_url, *reference_urls].compact
68
68
  end
69
69
 
70
70
  private
@@ -89,9 +89,9 @@ module RuboCop
89
89
  !urls.empty?
90
90
  end
91
91
 
92
- def reference_url
93
- url = cop_config['Reference']
94
- url.nil? || url.empty? ? nil : url
92
+ def reference_urls
93
+ urls = Array(cop_config['Reference'])
94
+ urls.nil? || urls.empty? ? nil : urls.reject(&:empty?)
95
95
  end
96
96
 
97
97
  def extra_details?
@@ -6,6 +6,7 @@ module RuboCop
6
6
  # This cop checks that the ABC size of methods is not higher than the
7
7
  # configured maximum. The ABC size is based on assignments, branches
8
8
  # (method calls), and conditions. See http://c2.com/cgi/wiki?AbcMetric
9
+ # and https://en.wikipedia.org/wiki/ABC_Software_Metric.
9
10
  class AbcSize < Cop
10
11
  include MethodComplexity
11
12
 
@@ -10,7 +10,7 @@ module RuboCop
10
10
  #
11
11
  # We separate the *calculator* from the *cop* so that the calculation,
12
12
  # the formula itself, is easier to test.
13
- module AbcSizeCalculator
13
+ class AbcSizeCalculator
14
14
  # > Branch -- an explicit forward program branch out of scope -- a
15
15
  # > function call, class method call ..
16
16
  # > http://c2.com/cgi/wiki?AbcMetric
@@ -22,21 +22,43 @@ module RuboCop
22
22
  CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES.freeze
23
23
 
24
24
  def self.calculate(node)
25
- assignment = 0
26
- branch = 0
27
- condition = 0
25
+ new(node).calculate
26
+ end
27
+
28
+ def initialize(node)
29
+ @assignment = 0
30
+ @branch = 0
31
+ @condition = 0
32
+ @node = node
33
+ end
28
34
 
29
- node.each_node do |child|
35
+ def calculate
36
+ @node.each_node do |child|
30
37
  if child.assignment?
31
- assignment += 1
38
+ @assignment += 1
32
39
  elsif BRANCH_NODES.include?(child.type)
33
- branch += 1
40
+ evaluate_branch_nodes(child)
34
41
  elsif CONDITION_NODES.include?(child.type)
35
- condition += 1
42
+ @condition += 1 if node_has_else_branch?(child)
43
+ @condition += 1
36
44
  end
37
45
  end
38
46
 
39
- Math.sqrt(assignment**2 + branch**2 + condition**2).round(2)
47
+ Math.sqrt(@assignment**2 + @branch**2 + @condition**2).round(2)
48
+ end
49
+
50
+ def evaluate_branch_nodes(node)
51
+ if node.comparison_method?
52
+ @condition += 1
53
+ else
54
+ @branch += 1
55
+ end
56
+ end
57
+
58
+ def node_has_else_branch?(node)
59
+ %i[case if].include?(node.type) &&
60
+ node.else? &&
61
+ node.loc.else.is?('else')
40
62
  end
41
63
  end
42
64
  end
@@ -30,7 +30,7 @@ module RuboCop
30
30
  #
31
31
  # return true if ruby_version >= 3.1
32
32
  #
33
- # And the above `ruby_version >= 3.1` is undecidedd whether it will be
33
+ # And the above `ruby_version >= 3.1` is undecided whether it will be
34
34
  # Ruby 3.1, 3.2, 4.0 or others.
35
35
  # See https://bugs.ruby-lang.org/issues/8976#note-41 for details.
36
36
  return false unless ruby_version >= 2.3
@@ -7,7 +7,7 @@ module RuboCop
7
7
  private
8
8
 
9
9
  def integer_part(node)
10
- node.source.sub(/^[+-]/, '').split('.').first
10
+ node.source.sub(/^[+-]/, '').split(/[eE.]/, 2).first
11
11
  end
12
12
  end
13
13
  end
@@ -18,7 +18,8 @@ module RuboCop
18
18
  default = default_cop_config['PreferredMethods']
19
19
  merged = cop_config['PreferredMethods']
20
20
  overrides = merged.values - default.values
21
- merged.reject { |key, _| overrides.include?(key) }.symbolize_keys
21
+ merged.reject { |key, _| overrides.include?(key) }
22
+ .map { |k, v| [k.to_sym, v] }.to_h
22
23
  end
23
24
  end
24
25
 
@@ -60,9 +60,14 @@ module RuboCop
60
60
 
61
61
  def allowed_method_call_on_rhs?(node)
62
62
  node && node.send_type? &&
63
- (node.receiver.nil? || !node.receiver.literal?)
63
+ (node.receiver.nil? || !literal_receiver?(node))
64
64
  end
65
65
 
66
+ def_node_matcher :literal_receiver?, <<-PATTERN
67
+ {(send literal? ...)
68
+ (send (begin literal?) ...)}
69
+ PATTERN
70
+
66
71
  def allowed_conditional_expression_on_rhs?(node)
67
72
  node && node.if_type? && contains_contant?(node)
68
73
  end
@@ -7,6 +7,9 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  # # bad
10
+ # def is_even(value)
11
+ # end
12
+ #
10
13
  # def is_even?(value)
11
14
  # end
12
15
  #
@@ -15,6 +18,9 @@ module RuboCop
15
18
  # end
16
19
  #
17
20
  # # bad
21
+ # def has_value
22
+ # end
23
+ #
18
24
  # def has_value?
19
25
  # end
20
26
  #