rubocop 1.6.0 → 1.9.0

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