rubocop 1.6.0 → 1.9.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +3 -2
  4. data/config/default.yml +142 -19
  5. data/lib/rubocop.rb +15 -1
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  7. data/lib/rubocop/comment_config.rb +6 -6
  8. data/lib/rubocop/config.rb +10 -7
  9. data/lib/rubocop/config_loader.rb +11 -14
  10. data/lib/rubocop/config_loader_resolver.rb +21 -4
  11. data/lib/rubocop/config_obsoletion.rb +5 -3
  12. data/lib/rubocop/config_obsoletion/extracted_cop.rb +6 -6
  13. data/lib/rubocop/config_store.rb +12 -1
  14. data/lib/rubocop/cop/base.rb +1 -1
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
  16. data/lib/rubocop/cop/generator.rb +1 -3
  17. data/lib/rubocop/cop/internal_affairs.rb +6 -1
  18. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  19. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  20. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  21. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  22. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +145 -0
  23. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  24. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +19 -3
  25. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +14 -0
  26. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  27. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
  28. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
  29. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  30. data/lib/rubocop/cop/layout/space_before_brackets.rb +62 -0
  31. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
  32. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
  33. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
  34. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
  35. data/lib/rubocop/cop/lint/deprecated_constants.rb +75 -0
  36. data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
  37. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
  38. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
  39. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  40. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  41. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  42. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
  43. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
  44. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
  45. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
  46. data/lib/rubocop/cop/lint/symbol_conversion.rb +102 -0
  47. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  48. data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
  49. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  50. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
  51. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  52. data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
  53. data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
  54. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  55. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
  56. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  57. data/lib/rubocop/cop/naming/variable_name.rb +2 -0
  58. data/lib/rubocop/cop/naming/variable_number.rb +2 -9
  59. data/lib/rubocop/cop/registry.rb +10 -0
  60. data/lib/rubocop/cop/severity.rb +3 -3
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
  62. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  63. data/lib/rubocop/cop/style/collection_methods.rb +14 -1
  64. data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
  65. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  66. data/lib/rubocop/cop/style/empty_literal.rb +6 -2
  67. data/lib/rubocop/cop/style/endless_method.rb +102 -0
  68. data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
  69. data/lib/rubocop/cop/style/explicit_block_argument.rb +10 -0
  70. data/lib/rubocop/cop/style/float_division.rb +3 -0
  71. data/lib/rubocop/cop/style/for.rb +2 -0
  72. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  73. data/lib/rubocop/cop/style/hash_except.rb +95 -0
  74. data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
  75. data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
  76. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +96 -0
  77. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
  78. data/lib/rubocop/cop/style/lambda_call.rb +2 -1
  79. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -0
  80. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
  81. data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
  82. data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
  83. data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
  84. data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
  85. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
  86. data/lib/rubocop/cop/style/nil_comparison.rb +1 -0
  87. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  88. data/lib/rubocop/cop/style/raise_args.rb +5 -2
  89. data/lib/rubocop/cop/style/redundant_argument.rb +7 -1
  90. data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
  91. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  92. data/lib/rubocop/cop/style/single_line_methods.rb +34 -2
  93. data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
  94. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  95. data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
  96. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  97. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  98. data/lib/rubocop/cop/util.rb +3 -1
  99. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  100. data/lib/rubocop/magic_comment.rb +30 -1
  101. data/lib/rubocop/options.rb +10 -10
  102. data/lib/rubocop/rspec/cop_helper.rb +0 -4
  103. data/lib/rubocop/rspec/expect_offense.rb +37 -22
  104. data/lib/rubocop/runner.rb +17 -1
  105. data/lib/rubocop/target_finder.rb +4 -2
  106. data/lib/rubocop/target_ruby.rb +47 -11
  107. data/lib/rubocop/util.rb +16 -0
  108. data/lib/rubocop/version.rb +8 -2
  109. metadata +26 -7
@@ -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
@@ -61,7 +61,8 @@ module RuboCop
61
61
  end
62
62
 
63
63
  def comment_range_with_surrounding_space(range)
64
- if previous_line_blank?(range)
64
+ if previous_line_blank?(range) &&
65
+ processed_source.comment_config.comment_only_line?(range.line)
65
66
  # When the previous line is blank, it should be retained
66
67
  range_with_surrounding_space(range: range, side: :right)
67
68
  else
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Sort globbed results by default in Ruby 3.0.
7
+ # This cop checks for redundant `sort` method to `Dir.glob` and `Dir[]`.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # Dir.glob('./lib/**/*.rb').sort.each do |file|
13
+ # end
14
+ #
15
+ # Dir['./lib/**/*.rb'].sort.each do |file|
16
+ # end
17
+ #
18
+ # # good
19
+ # Dir.glob('./lib/**/*.rb').each do |file|
20
+ # end
21
+ #
22
+ # Dir['./lib/**/*.rb'].each do |file|
23
+ # end
24
+ #
25
+ class RedundantDirGlobSort < Base
26
+ extend AutoCorrector
27
+ extend TargetRubyVersion
28
+
29
+ minimum_target_ruby_version 3.0
30
+
31
+ MSG = 'Remove redundant `sort`.'
32
+ RESTRICT_ON_SEND = %i[sort].freeze
33
+ GLOB_METHODS = %i[glob []].freeze
34
+
35
+ def on_send(node)
36
+ return unless (receiver = node.receiver)
37
+ return unless receiver.receiver&.const_type? && receiver.receiver.short_name == :Dir
38
+ return unless GLOB_METHODS.include?(receiver.method_name)
39
+
40
+ selector = node.loc.selector
41
+
42
+ add_offense(selector) do |corrector|
43
+ corrector.remove(selector)
44
+ corrector.remove(node.loc.dot)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -8,47 +8,66 @@ module RuboCop
8
8
  # @example
9
9
  #
10
10
  # # bad
11
- #
12
11
  # a = *[1, 2, 3]
13
12
  # a = *'a'
14
13
  # a = *1
15
- #
16
- # begin
17
- # foo
18
- # rescue *[StandardError, ApplicationError]
19
- # bar
20
- # end
21
- #
22
- # case foo
23
- # when *[1, 2, 3]
24
- # bar
25
- # else
26
- # baz
27
- # end
28
- #
29
- # @example
14
+ # ['a', 'b', *%w(c d e), 'f', 'g']
30
15
  #
31
16
  # # good
32
- #
33
17
  # c = [1, 2, 3]
34
18
  # a = *c
35
19
  # a, b = *c
36
20
  # a, *b = *c
37
21
  # a = *1..10
38
22
  # a = ['a']
23
+ # ['a', 'b', 'c', 'd', 'e', 'f', 'g']
39
24
  #
25
+ # # bad
26
+ # do_something(*['foo', 'bar', 'baz'])
27
+ #
28
+ # # good
29
+ # do_something('foo', 'bar', 'baz')
30
+ #
31
+ # # bad
32
+ # begin
33
+ # foo
34
+ # rescue *[StandardError, ApplicationError]
35
+ # bar
36
+ # end
37
+ #
38
+ # # good
40
39
  # begin
41
40
  # foo
42
41
  # rescue StandardError, ApplicationError
43
42
  # bar
44
43
  # end
45
44
  #
45
+ # # bad
46
+ # case foo
47
+ # when *[1, 2, 3]
48
+ # bar
49
+ # else
50
+ # baz
51
+ # end
52
+ #
53
+ # # good
46
54
  # case foo
47
55
  # when 1, 2, 3
48
56
  # bar
49
57
  # else
50
58
  # baz
51
59
  # end
60
+ #
61
+ # @example AllowPercentLiteralArrayArgument: true (default)
62
+ #
63
+ # # good
64
+ # do_something(*%w[foo bar baz])
65
+ #
66
+ # @example AllowPercentLiteralArrayArgument: false
67
+ #
68
+ # # bad
69
+ # do_something(*%w[foo bar baz])
70
+ #
52
71
  class RedundantSplatExpansion < Base
53
72
  extend AutoCorrector
54
73
 
@@ -75,6 +94,9 @@ module RuboCop
75
94
  redundant_splat_expansion(node) do
76
95
  if array_splat?(node) &&
77
96
  (method_argument?(node) || part_of_an_array?(node))
97
+ return if allow_percent_literal_array_argument? &&
98
+ use_percent_literal_array_argument?(node)
99
+
78
100
  add_offense(node, message: ARRAY_PARAM_MSG) do |corrector|
79
101
  autocorrect(corrector, node)
80
102
  end
@@ -170,6 +192,17 @@ module RuboCop
170
192
  elements.join(', ')
171
193
  end
172
194
  end
195
+
196
+ def use_percent_literal_array_argument?(node)
197
+ argument = node.children.first
198
+
199
+ node.parent.send_type? &&
200
+ (argument.percent_literal?(:string) || argument.percent_literal?(:symbol))
201
+ end
202
+
203
+ def allow_percent_literal_array_argument?
204
+ cop_config.fetch('AllowPercentLiteralArrayArgument', true)
205
+ end
173
206
  end
174
207
  end
175
208
  end
@@ -106,23 +106,13 @@ module RuboCop
106
106
  error && error.ancestors[1] == SystemCallError
107
107
  end
108
108
 
109
- def silence_warnings
110
- # Replaces Kernel::silence_warnings since it hides any warnings,
111
- # including the RuboCop ones
112
- old_verbose = $VERBOSE
113
- $VERBOSE = nil
114
- yield
115
- ensure
116
- $VERBOSE = old_verbose
117
- end
118
-
119
109
  def evaluate_exceptions(group)
120
110
  rescued_exceptions = group.exceptions
121
111
 
122
112
  if rescued_exceptions.any?
123
113
  rescued_exceptions.each_with_object([]) do |exception, converted|
124
114
  begin
125
- silence_warnings do
115
+ RuboCop::Util.silence_warnings do
126
116
  # Avoid printing deprecation warnings about constants
127
117
  converted << Kernel.const_get(exception.source)
128
118
  end
@@ -0,0 +1,102 @@
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.str_type? || node.receiver.sym_type?
32
+
33
+ register_offense(node, correction: node.receiver.value.to_sym.inspect)
34
+ end
35
+
36
+ def on_sym(node)
37
+ return if properly_quoted?(node.source, node.value.inspect)
38
+
39
+ # `alias` arguments are symbols but since a symbol that requires
40
+ # being quoted is not a valid method identifier, it can be ignored
41
+ return if in_alias?(node)
42
+
43
+ # The `%I[]` and `%i[]` macros are parsed as normal arrays of symbols
44
+ # so they need to be ignored.
45
+ return if in_percent_literal_array?(node)
46
+
47
+ # Symbol hash keys have a different format and need to be handled separately
48
+ return correct_hash_key(node) if hash_key?(node)
49
+
50
+ register_offense(node, correction: node.value.inspect)
51
+ end
52
+
53
+ private
54
+
55
+ def register_offense(node, correction:, message: format(MSG, correction: correction))
56
+ add_offense(node, message: message) do |corrector|
57
+ corrector.replace(node, correction)
58
+ end
59
+ end
60
+
61
+ def properly_quoted?(source, value)
62
+ return true unless source.match?(/['"]/)
63
+
64
+ source == value ||
65
+ # `Symbol#inspect` uses double quotes, but allow single-quoted
66
+ # symbols to work as well.
67
+ source.tr("'", '"') == value
68
+ end
69
+
70
+ def in_alias?(node)
71
+ node.parent&.alias_type?
72
+ end
73
+
74
+ def in_percent_literal_array?(node)
75
+ node.parent&.array_type? && node.parent&.percent_literal?
76
+ end
77
+
78
+ def hash_key?(node)
79
+ node.parent&.pair_type? && node == node.parent.child_nodes.first
80
+ end
81
+
82
+ def correct_hash_key(node)
83
+ # Although some operators can be converted to symbols normally
84
+ # (ie. `:==`), these are not accepted as hash keys and will
85
+ # raise a syntax error (eg. `{ ==: ... }`). Therefore, if the
86
+ # symbol does not start with an alpha-numeric or underscore, it
87
+ # will be ignored.
88
+ return unless node.value.to_s.match?(/\A[a-z0-9_]/i)
89
+
90
+ correction = node.value.inspect.delete(':')
91
+ return if properly_quoted?(node.source, correction)
92
+
93
+ register_offense(
94
+ node,
95
+ correction: correction,
96
+ message: format(MSG, correction: "#{correction}:")
97
+ )
98
+ end
99
+ end
100
+ end
101
+ end
102
+ 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