rubocop 0.65.0 → 0.66.0

Sign up to get free protection for your applications and to get access to all the features.
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
  #