rubocop 1.8.1 → 1.9.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +37 -4
  4. data/lib/rubocop.rb +6 -0
  5. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  6. data/lib/rubocop/config.rb +5 -2
  7. data/lib/rubocop/config_loader.rb +7 -14
  8. data/lib/rubocop/config_store.rb +12 -1
  9. data/lib/rubocop/cop/base.rb +2 -1
  10. data/lib/rubocop/cop/exclude_limit.rb +26 -0
  11. data/lib/rubocop/cop/generator.rb +1 -3
  12. data/lib/rubocop/cop/internal_affairs.rb +5 -1
  13. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  14. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  15. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  16. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  17. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  18. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +37 -17
  19. data/lib/rubocop/cop/layout/first_argument_indentation.rb +16 -2
  20. data/lib/rubocop/cop/layout/line_length.rb +2 -1
  21. data/lib/rubocop/cop/layout/space_before_brackets.rb +9 -4
  22. data/lib/rubocop/cop/lint/deprecated_constants.rb +5 -0
  23. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  24. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  25. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  26. data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
  27. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  28. data/lib/rubocop/cop/message_annotator.rb +4 -1
  29. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
  30. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  31. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  32. data/lib/rubocop/cop/mixin/code_length.rb +3 -1
  33. data/lib/rubocop/cop/mixin/comments_help.rb +0 -1
  34. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
  35. data/lib/rubocop/cop/mixin/method_complexity.rb +3 -1
  36. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  37. data/lib/rubocop/cop/naming/variable_number.rb +1 -1
  38. data/lib/rubocop/cop/severity.rb +3 -3
  39. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  40. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  41. data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
  42. data/lib/rubocop/cop/style/float_division.rb +3 -0
  43. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  44. data/lib/rubocop/cop/style/if_inside_else.rb +14 -7
  45. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +120 -0
  46. data/lib/rubocop/cop/style/nil_comparison.rb +3 -0
  47. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  48. data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
  49. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  50. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  51. data/lib/rubocop/cop/style/sole_nested_conditional.rb +26 -2
  52. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  53. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  54. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
  55. data/lib/rubocop/magic_comment.rb +30 -1
  56. data/lib/rubocop/options.rb +1 -1
  57. data/lib/rubocop/rspec/expect_offense.rb +5 -2
  58. data/lib/rubocop/runner.rb +1 -0
  59. data/lib/rubocop/version.rb +2 -2
  60. metadata +13 -3
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for uses of numbered parameter assignment.
7
+ # It emulates the following warning in Ruby 2.7:
8
+ #
9
+ # % ruby -ve '_1 = :value'
10
+ # ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
11
+ # -e:1: warning: `_1' is reserved for numbered parameter; consider another name
12
+ #
13
+ # Assiging to numbered parameter (from `_1` to `_9`) cause an error in Ruby 3.0.
14
+ #
15
+ # % ruby -ve '_1 = :value'
16
+ # ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
17
+ # -e:1: _1 is reserved for numbered parameter
18
+ #
19
+ # NOTE: The parametered parameters are from `_1` to `_9`. This cop checks `_0`, and over `_10`
20
+ # as well to prevent confusion.
21
+ #
22
+ # @example
23
+ #
24
+ # # bad
25
+ # _1 = :value
26
+ #
27
+ # # good
28
+ # non_numbered_parameter_name = :value
29
+ #
30
+ class NumberedParameterAssignment < Base
31
+ NUM_PARAM_MSG = '`_%<number>s` is reserved for numbered parameter; consider another name.'
32
+ LVAR_MSG = '`_%<number>s` is similar to numbered parameter; consider another name.'
33
+ NUMBERED_PARAMETER_RANGE = (1..9).freeze
34
+
35
+ def on_lvasgn(node)
36
+ lhs, _rhs = *node
37
+ return unless /\A_(\d+)\z/ =~ lhs
38
+
39
+ number = Regexp.last_match(1).to_i
40
+ template = NUMBERED_PARAMETER_RANGE.include?(number) ? NUM_PARAM_MSG : LVAR_MSG
41
+
42
+ add_offense(node, message: format(template, number: number))
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for unintended or-assignment to a constant.
7
+ #
8
+ # Constants should always be assigned in the same location. And its value
9
+ # should always be the same. If constants are assigned in multiple
10
+ # locations, the result may vary depending on the order of `require`.
11
+ #
12
+ # Also, if you already have such an implementation, auto-correction may
13
+ # change the result.
14
+ #
15
+ # @example
16
+ #
17
+ # # bad
18
+ # CONST ||= 1
19
+ #
20
+ # # good
21
+ # CONST = 1
22
+ #
23
+ class OrAssignmentToConstant < Base
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Avoid using or-assignment with constants.'
27
+
28
+ def on_or_asgn(node)
29
+ lhs, _rhs = *node
30
+ return unless lhs&.casgn_type?
31
+
32
+ add_offense(node.loc.operator) do |corrector|
33
+ corrector.replace(node.loc.operator, '=')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for uses of literal strings converted to
7
+ # a symbol where a literal symbol could be used instead.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # 'string'.to_sym
12
+ # :symbol.to_sym
13
+ # 'underscored_string'.to_sym
14
+ # :'underscored_symbol'
15
+ # 'hyphenated-string'.to_sym
16
+ #
17
+ # # good
18
+ # :string
19
+ # :symbol
20
+ # :underscored_string
21
+ # :underscored_symbol
22
+ # :'hyphenated-string'
23
+ #
24
+ class SymbolConversion < Base
25
+ extend AutoCorrector
26
+
27
+ MSG = 'Unnecessary symbol conversion; use `%<correction>s` instead.'
28
+ RESTRICT_ON_SEND = %i[to_sym intern].freeze
29
+
30
+ def on_send(node)
31
+ return unless node.receiver
32
+ return unless node.receiver.str_type? || node.receiver.sym_type?
33
+
34
+ register_offense(node, correction: node.receiver.value.to_sym.inspect)
35
+ end
36
+
37
+ def on_sym(node)
38
+ return if properly_quoted?(node.source, node.value.inspect)
39
+
40
+ # `alias` arguments are symbols but since a symbol that requires
41
+ # being quoted is not a valid method identifier, it can be ignored
42
+ return if in_alias?(node)
43
+
44
+ # The `%I[]` and `%i[]` macros are parsed as normal arrays of symbols
45
+ # so they need to be ignored.
46
+ return if in_percent_literal_array?(node)
47
+
48
+ # Symbol hash keys have a different format and need to be handled separately
49
+ return correct_hash_key(node) if hash_key?(node)
50
+
51
+ register_offense(node, correction: node.value.inspect)
52
+ end
53
+
54
+ private
55
+
56
+ def register_offense(node, correction:, message: format(MSG, correction: correction))
57
+ add_offense(node, message: message) do |corrector|
58
+ corrector.replace(node, correction)
59
+ end
60
+ end
61
+
62
+ def properly_quoted?(source, value)
63
+ return true if !source.match?(/['"]/) || value.end_with?('=')
64
+
65
+ source == value ||
66
+ # `Symbol#inspect` uses double quotes, but allow single-quoted
67
+ # symbols to work as well.
68
+ source.tr("'", '"') == value
69
+ end
70
+
71
+ def in_alias?(node)
72
+ node.parent&.alias_type?
73
+ end
74
+
75
+ def in_percent_literal_array?(node)
76
+ node.parent&.array_type? && node.parent&.percent_literal?
77
+ end
78
+
79
+ def hash_key?(node)
80
+ node.parent&.pair_type? && node == node.parent.child_nodes.first
81
+ end
82
+
83
+ def correct_hash_key(node)
84
+ # Although some operators can be converted to symbols normally
85
+ # (ie. `:==`), these are not accepted as hash keys and will
86
+ # raise a syntax error (eg. `{ ==: ... }`). Therefore, if the
87
+ # symbol does not start with an alpha-numeric or underscore, it
88
+ # will be ignored.
89
+ return unless node.value.to_s.match?(/\A[a-z0-9_]/i)
90
+
91
+ correction = node.value.inspect.gsub(/\A:/, '')
92
+ return if properly_quoted?(node.source, correction)
93
+
94
+ register_offense(
95
+ node,
96
+ correction: correction,
97
+ message: format(MSG, correction: "#{correction}:")
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for "triple quotes" (strings delimted by any odd number
7
+ # of quotes greater than 1).
8
+ #
9
+ # Ruby allows multiple strings to be implicitly concatenated by just
10
+ # being adjacent in a statement (ie. `"foo""bar" == "foobar"`). This sometimes
11
+ # gives the impression that there is something special about triple quotes, but
12
+ # in fact it is just extra unnecessary quotes and produces the same string. Each
13
+ # pair of quotes produces an additional concatenated empty string, so the result
14
+ # is still only the "actual" string within the delimiters.
15
+ #
16
+ # NOTE: Although this cop is called triple quotes, the same behavior is present
17
+ # for strings delimited by 5, 7, etc. quotation marks.
18
+ #
19
+ # @example
20
+ # # bad
21
+ # """
22
+ # A string
23
+ # """
24
+ #
25
+ # # bad
26
+ # '''
27
+ # A string
28
+ # '''
29
+ #
30
+ # # good
31
+ # "
32
+ # A string
33
+ # "
34
+ #
35
+ # # good
36
+ # <<STRING
37
+ # A string
38
+ # STRING
39
+ #
40
+ # # good (but not the same spacing as the bad case)
41
+ # 'A string'
42
+ class TripleQuotes < Base
43
+ extend AutoCorrector
44
+
45
+ MSG = 'Delimiting a string with multiple quotes has no effect, use a single quote instead.'
46
+
47
+ def on_dstr(node)
48
+ return if (empty_str_nodes = empty_str_nodes(node)).none?
49
+
50
+ opening_quotes = node.source.scan(/(?<=\A)['"]*/)[0]
51
+ return if opening_quotes.size < 3
52
+
53
+ # If the node is composed of only empty `str` nodes, keep one
54
+ empty_str_nodes.shift if empty_str_nodes.size == node.child_nodes.size
55
+
56
+ add_offense(node) do |corrector|
57
+ empty_str_nodes.each do |str|
58
+ corrector.remove(str)
59
+ end
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def empty_str_nodes(node)
66
+ node.each_child_node(:str).select { |str| str.value == '' }
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -85,8 +85,11 @@ module RuboCop
85
85
  end
86
86
  end
87
87
 
88
+ # Returns the base style guide URL from AllCops or the specific department
89
+ #
90
+ # @return [String] style guide URL
88
91
  def style_guide_base_url
89
- department_name = cop_name.split('/').first
92
+ department_name = cop_name.split('/')[0..-2].join('/')
90
93
 
91
94
  config.for_department(department_name)['StyleGuideBaseURL'] ||
92
95
  config.for_all_cops['StyleGuideBaseURL']
@@ -12,13 +12,13 @@ module RuboCop
12
12
  #
13
13
  # The maximum level of nesting allowed is configurable.
14
14
  class BlockNesting < Base
15
- include ConfigurableMax
16
-
17
15
  NESTING_BLOCKS = %i[
18
16
  case if while while_post
19
17
  until until_post for resbody
20
18
  ].freeze
21
19
 
20
+ exclude_limit 'Max'
21
+
22
22
  def on_new_investigation
23
23
  return if processed_source.blank?
24
24
 
@@ -51,7 +51,8 @@ module RuboCop
51
51
  # end
52
52
  #
53
53
  class ParameterLists < Base
54
- include ConfigurableMax
54
+ exclude_limit 'Max'
55
+ exclude_limit 'MaxOptionalParameters'
55
56
 
56
57
  MSG = 'Avoid parameter lists longer than %<max>d parameters. ' \
57
58
  '[%<count>d/%<max>d]'
@@ -70,7 +71,9 @@ module RuboCop
70
71
  count: optargs.count
71
72
  )
72
73
 
73
- add_offense(node, message: message)
74
+ add_offense(node, message: message) do
75
+ self.max_optional_parameters = optargs.count
76
+ end
74
77
  end
75
78
  alias on_defs on_def
76
79
 
@@ -69,6 +69,11 @@ module RuboCop
69
69
  # @api private
70
70
  def extract_first_element_over_column_limit(node, elements, max)
71
71
  line = node.first_line
72
+
73
+ # If the first argument is a hash pair but the method is not parenthesized,
74
+ # the argument cannot be moved to another line because it cause a syntax error.
75
+ elements.shift if node.send_type? && !node.parenthesized? && elements.first.pair_type?
76
+
72
77
  i = 0
73
78
  i += 1 while within_column_limit?(elements[i], max, line)
74
79
  return elements.first if i.zero?
@@ -4,10 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for checking length of code segments.
6
6
  module CodeLength
7
- include ConfigurableMax
7
+ extend ExcludeLimit
8
8
 
9
9
  MSG = '%<label>s has too many lines. [%<length>d/%<max>d]'
10
10
 
11
+ exclude_limit 'Max'
12
+
11
13
  private
12
14
 
13
15
  def message(length, max_length)
@@ -9,7 +9,6 @@ module RuboCop
9
9
  def source_range_with_comment(node)
10
10
  begin_pos = begin_pos_with_comment(node)
11
11
  end_pos = end_position_for(node)
12
- end_pos += 1 if node.def_type?
13
12
 
14
13
  Parser::Source::Range.new(buffer, begin_pos, end_pos)
15
14
  end
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  # Handles `Max` configuration parameters, especially setting them to an
6
6
  # appropriate value with --auto-gen-config.
7
+ # @deprecated Use `exclude_limit ParameterName` instead.
7
8
  module ConfigurableMax
8
9
  private
9
10
 
@@ -6,10 +6,12 @@ module RuboCop
6
6
  #
7
7
  # This module handles measurement and reporting of complexity in methods.
8
8
  module MethodComplexity
9
- include ConfigurableMax
10
9
  include IgnoredMethods
11
10
  include Metrics::Utils::RepeatedCsendDiscount
12
11
  extend NodePattern::Macros
12
+ extend ExcludeLimit
13
+
14
+ exclude_limit 'Max'
13
15
 
14
16
  # Ensure cops that include `MethodComplexity` have the config
15
17
  # `attr_accessor`s that `ignored_method?` needs.
@@ -71,11 +71,7 @@ module RuboCop
71
71
  add_offense(range, message: message) do |corrector|
72
72
  corrector.replace(range, preferred_name)
73
73
 
74
- node.body&.each_descendant(:lvar) do |var|
75
- next unless var.children.first == offending_name
76
-
77
- corrector.replace(var, preferred_name)
78
- end
74
+ correct_node(corrector, node.body, offending_name, preferred_name)
79
75
  end
80
76
  end
81
77
 
@@ -86,6 +82,43 @@ module RuboCop
86
82
  variable.loc.expression
87
83
  end
88
84
 
85
+ def variable_name_matches?(node, name)
86
+ if node.masgn_type?
87
+ node.each_descendant(:lvasgn).any? do |lvasgn_node|
88
+ variable_name_matches?(lvasgn_node, name)
89
+ end
90
+ else
91
+ node.children.first == name
92
+ end
93
+ end
94
+
95
+ def correct_node(corrector, node, offending_name, preferred_name)
96
+ return unless node
97
+
98
+ node.each_node(:lvar, :lvasgn, :masgn) do |child_node|
99
+ next unless variable_name_matches?(child_node, offending_name)
100
+
101
+ corrector.replace(child_node, preferred_name) if child_node.lvar_type?
102
+
103
+ if child_node.masgn_type? || child_node.lvasgn_type?
104
+ correct_reassignment(corrector, child_node, offending_name, preferred_name)
105
+ break
106
+ end
107
+ end
108
+ end
109
+
110
+ # If the exception variable is reassigned, that assignment needs to be corrected.
111
+ # Further `lvar` nodes will not be corrected though since they now refer to a
112
+ # different variable.
113
+ def correct_reassignment(corrector, node, offending_name, preferred_name)
114
+ if node.lvasgn_type?
115
+ correct_node(corrector, node.child_nodes.first, offending_name, preferred_name)
116
+ elsif node.masgn_type?
117
+ # With multiple assign, the assignments are in an array as the last child
118
+ correct_node(corrector, node.children.last, offending_name, preferred_name)
119
+ end
120
+ end
121
+
89
122
  def preferred_name(variable_name)
90
123
  preferred_name = cop_config.fetch('PreferredName', 'e')
91
124
  if variable_name.to_s.start_with?('_')
@@ -92,7 +92,7 @@ module RuboCop
92
92
  # # good
93
93
  # :some_sym_1
94
94
  #
95
- # @example AllowedIdentifier: [capture3]
95
+ # @example AllowedIdentifiers: [capture3]
96
96
  # # good
97
97
  # expect(Open3).to receive(:capture3)
98
98
  #
@@ -6,10 +6,10 @@ module RuboCop
6
6
  class Severity
7
7
  include Comparable
8
8
 
9
- NAMES = %i[refactor convention warning error fatal].freeze
9
+ NAMES = %i[info refactor convention warning error fatal].freeze
10
10
 
11
11
  # @api private
12
- CODE_TABLE = { R: :refactor, C: :convention,
12
+ CODE_TABLE = { I: :info, R: :refactor, C: :convention,
13
13
  W: :warning, E: :error, F: :fatal }.freeze
14
14
 
15
15
  # @api public
@@ -18,7 +18,7 @@ module RuboCop
18
18
  #
19
19
  # @return [Symbol]
20
20
  # severity.
21
- # any of `:refactor`, `:convention`, `:warning`, `:error` or `:fatal`.
21
+ # any of `:info`, `:refactor`, `:convention`, `:warning`, `:error` or `:fatal`.
22
22
  attr_reader :name
23
23
 
24
24
  def self.name_from_code(code)