rubocop 1.35.1 → 1.37.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +32 -1
  4. data/lib/rubocop/arguments_env.rb +17 -0
  5. data/lib/rubocop/arguments_file.rb +17 -0
  6. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  7. data/lib/rubocop/cop/generator.rb +1 -2
  8. data/lib/rubocop/cop/layout/block_alignment.rb +14 -12
  9. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  10. data/lib/rubocop/cop/layout/indentation_width.rb +4 -2
  11. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  12. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +25 -9
  13. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +28 -3
  14. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -2
  15. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  16. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  17. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  18. data/lib/rubocop/cop/lint/duplicate_require.rb +1 -1
  19. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  20. data/lib/rubocop/cop/lint/empty_conditional_body.rb +46 -4
  21. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  22. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  23. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  24. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  25. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
  26. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  27. data/lib/rubocop/cop/lint/redundant_require_statement.rb +29 -9
  28. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  29. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  30. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
  31. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +17 -2
  32. data/lib/rubocop/cop/lint/unreachable_loop.rb +2 -2
  33. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  34. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  35. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
  36. data/lib/rubocop/cop/mixin/allowed_methods.rb +10 -5
  37. data/lib/rubocop/cop/mixin/allowed_pattern.rb +13 -5
  38. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  39. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  40. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  41. data/lib/rubocop/cop/mixin/hash_transform_method.rb +9 -5
  42. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  43. data/lib/rubocop/cop/mixin/surrounding_space.rb +6 -5
  44. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  45. data/lib/rubocop/cop/style/access_modifier_declarations.rb +97 -1
  46. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  47. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  48. data/lib/rubocop/cop/style/case_equality.rb +40 -10
  49. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  50. data/lib/rubocop/cop/style/collection_compact.rb +6 -1
  51. data/lib/rubocop/cop/style/each_for_simple_loop.rb +40 -5
  52. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  53. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  54. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  55. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  56. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  57. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  58. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  59. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  60. data/lib/rubocop/cop/style/operator_method_call.rb +39 -0
  61. data/lib/rubocop/cop/style/perl_backrefs.rb +22 -1
  62. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  63. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  64. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  65. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  66. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  67. data/lib/rubocop/cop/style/redundant_string_escape.rb +173 -0
  68. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  69. data/lib/rubocop/cop/style/static_class.rb +32 -1
  70. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  71. data/lib/rubocop/cop/style/symbol_proc.rb +6 -5
  72. data/lib/rubocop/cop/style/word_array.rb +2 -0
  73. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  74. data/lib/rubocop/options.rb +13 -13
  75. data/lib/rubocop/rspec/shared_contexts.rb +13 -1
  76. data/lib/rubocop/runner.rb +4 -0
  77. data/lib/rubocop/server/cache.rb +5 -1
  78. data/lib/rubocop/server/cli.rb +9 -2
  79. data/lib/rubocop/server/client_command/exec.rb +5 -0
  80. data/lib/rubocop/server/core.rb +2 -1
  81. data/lib/rubocop/server/socket_reader.rb +5 -1
  82. data/lib/rubocop/server.rb +1 -1
  83. data/lib/rubocop/version.rb +8 -2
  84. data/lib/rubocop.rb +3 -0
  85. metadata +10 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52ce553282d97fbec24636f7104e3c1c1ceb32b27610542ae22470d84a6421ce
4
- data.tar.gz: 6176cfc6699491e9d755e323a813964c1dd244a16d61ad2545e93f8109d69fa6
3
+ metadata.gz: e928a8547def9cafd9cddc36b5a407aef7d46fdf9825941c7906073492d88668
4
+ data.tar.gz: 3ef9727850f93815ba310b7c3a8cf74e53a0ee18a0fcf6731fd97a3640ffe28d
5
5
  SHA512:
6
- metadata.gz: 69e648784e186575033e4f8b65be6f5fb8f456e557d597e5321bbe0f70d9aef39b0a4c974347d13498b5e88c664177b72aae809b5f39bec98292cb0e7a4fb18a
7
- data.tar.gz: 866c506305659953fe31ea005c51d936dcfed82429e7582f7d64daab5ea5b077ef1fb619f7932c204749582dc1bec695e090c32d5b81450984d30e7a2e3125b4
6
+ metadata.gz: f57e5d25375fe47734a6e198924db74b341501762dc8f54277a888788ab821215c96847940cec1af4d90673fe79ab906b451bf25bec90f9c8c080e78efc01111
7
+ data.tar.gz: 69cafbca956eaa49887596d255f6888057ede94f3621349398de95da5c477201f7bee7eae074ab4750ce0131dcb26d6dfdc77c31f5fcb478d2fc25d5ca73e68c
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.35', require: false
56
+ gem 'rubocop', '~> 1.37', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -1707,6 +1707,11 @@ Lint/DuplicateHashKey:
1707
1707
  VersionAdded: '0.34'
1708
1708
  VersionChanged: '0.77'
1709
1709
 
1710
+ Lint/DuplicateMagicComment:
1711
+ Description: 'Check for duplicated magic comments.'
1712
+ Enabled: pending
1713
+ VersionAdded: '1.37'
1714
+
1710
1715
  Lint/DuplicateMethods:
1711
1716
  Description: 'Check for duplicate method definitions.'
1712
1717
  Enabled: true
@@ -1957,6 +1962,8 @@ Lint/NestedMethodDefinition:
1957
1962
  Description: 'Do not use nested method definitions.'
1958
1963
  StyleGuide: '#no-nested-methods'
1959
1964
  Enabled: true
1965
+ AllowedMethods: []
1966
+ AllowedPatterns: []
1960
1967
  VersionAdded: '0.32'
1961
1968
 
1962
1969
  Lint/NestedPercentLiteral:
@@ -2021,7 +2028,9 @@ Lint/OrAssignmentToConstant:
2021
2028
  Lint/OrderedMagicComments:
2022
2029
  Description: 'Checks the proper ordering of magic comments and whether a magic comment is not placed before a shebang.'
2023
2030
  Enabled: true
2031
+ SafeAutoCorrect: false
2024
2032
  VersionAdded: '0.53'
2033
+ VersionChanged: '1.37'
2025
2034
 
2026
2035
  Lint/OutOfRangeRegexpRef:
2027
2036
  Description: 'Checks for out of range reference for Regexp because it always returns nil.'
@@ -2794,6 +2803,7 @@ Naming/MethodParameterName:
2794
2803
  AllowNamesEndingInNumbers: true
2795
2804
  # Allowed names that will not register an offense
2796
2805
  AllowedNames:
2806
+ - as
2797
2807
  - at
2798
2808
  - by
2799
2809
  - db
@@ -2948,6 +2958,7 @@ Style/AccessModifierDeclarations:
2948
2958
  - inline
2949
2959
  - group
2950
2960
  AllowModifiersOnSymbols: true
2961
+ SafeAutoCorrect: false
2951
2962
 
2952
2963
  Style/AccessorGrouping:
2953
2964
  Description: 'Checks for grouping of accessors in `class` and `module` bodies.'
@@ -3186,6 +3197,15 @@ Style/CaseEquality:
3186
3197
  # # good
3187
3198
  # String === "string"
3188
3199
  AllowOnConstant: false
3200
+ # If `AllowOnSelfClass` option is enabled, the cop will ignore violations when the receiver of
3201
+ # the case equality operator is `self.class`.
3202
+ #
3203
+ # # bad
3204
+ # some_class === object
3205
+ #
3206
+ # # good
3207
+ # self.class === object
3208
+ AllowOnSelfClass: false
3189
3209
 
3190
3210
  Style/CaseLikeIf:
3191
3211
  Description: 'Identifies places where `if-elsif` constructions can be replaced with `case-when`.'
@@ -3839,7 +3859,7 @@ Style/HashSyntax:
3839
3859
  - never
3840
3860
  # accepts both shorthand and explicit use of hash literal value.
3841
3861
  - either
3842
- # like "always", but will avoid mixing styles in a single hash
3862
+ # like "either", but will avoid mixing styles in a single hash
3843
3863
  - consistent
3844
3864
  # Force hashes that have a symbol value to use hash rockets
3845
3865
  UseHashRocketsWithSymbolValues: false
@@ -4487,6 +4507,12 @@ Style/OpenStructUse:
4487
4507
  Enabled: pending
4488
4508
  VersionAdded: '1.23'
4489
4509
 
4510
+ Style/OperatorMethodCall:
4511
+ Description: 'Checks for redundant dot before operator method call.'
4512
+ StyleGuide: '#operator-method-call'
4513
+ Enabled: pending
4514
+ VersionAdded: '1.37'
4515
+
4490
4516
  Style/OptionHash:
4491
4517
  Description: "Don't use option hashes when you can use keyword arguments."
4492
4518
  Enabled: false
@@ -4781,6 +4807,11 @@ Style/RedundantSortBy:
4781
4807
  Enabled: true
4782
4808
  VersionAdded: '0.36'
4783
4809
 
4810
+ Style/RedundantStringEscape:
4811
+ Description: 'Checks for redundant escapes in string literals.'
4812
+ Enabled: pending
4813
+ VersionAdded: '1.37'
4814
+
4784
4815
  Style/RegexpLiteral:
4785
4816
  Description: 'Use / or %r around regular expressions.'
4786
4817
  StyleGuide: '#percent-r'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This is a class that reads optional command line arguments to rubocop from environment variable.
5
+ # @api private
6
+ class ArgumentsEnv
7
+ def self.read_as_arguments
8
+ if (arguments = ENV.fetch('RUBOCOP_OPTS', '')).empty?
9
+ []
10
+ else
11
+ require 'shellwords'
12
+
13
+ Shellwords.split(arguments)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This is a class that reads optional command line arguments to rubocop from .rubocop file.
5
+ # @api private
6
+ class ArgumentsFile
7
+ def self.read_as_arguments
8
+ if File.exist?('.rubocop') && !File.directory?('.rubocop')
9
+ require 'shellwords'
10
+
11
+ File.read('.rubocop').shellsplit
12
+ else
13
+ []
14
+ end
15
+ end
16
+ end
17
+ end
@@ -41,13 +41,13 @@ module RuboCop
41
41
 
42
42
  def with_redirect
43
43
  if @options[:stderr]
44
- orig_stdout = $stdout.dup
45
- $stdout.reopen($stderr)
46
-
47
- result = yield
48
-
49
- $stdout.reopen(orig_stdout)
50
- result
44
+ orig_stdout = $stdout
45
+ begin
46
+ $stdout = $stderr
47
+ yield
48
+ ensure
49
+ $stdout = orig_stdout
50
+ end
51
51
  else
52
52
  yield
53
53
  end
@@ -206,9 +206,8 @@ module RuboCop
206
206
  end
207
207
 
208
208
  def snake_case(camel_case_string)
209
- return 'rspec' if camel_case_string == 'RSpec'
210
-
211
209
  camel_case_string
210
+ .gsub('RSpec', 'Rspec')
212
211
  .gsub(%r{([^A-Z/])([A-Z]+)}, '\1_\2')
213
212
  .gsub(%r{([A-Z])([A-Z][^A-Z\d/]+)}, '\1_\2')
214
213
  .downcase
@@ -22,23 +22,24 @@ module RuboCop
22
22
  # # bad
23
23
  #
24
24
  # foo.bar
25
- # .each do
26
- # baz
27
- # end
25
+ # .each do
26
+ # baz
27
+ # end
28
28
  #
29
29
  # # good
30
30
  #
31
- # variable = lambda do |i|
32
- # i
31
+ # foo.bar
32
+ # .each do
33
+ # baz
33
34
  # end
34
35
  #
35
36
  # @example EnforcedStyleAlignWith: start_of_block
36
37
  # # bad
37
38
  #
38
39
  # foo.bar
39
- # .each do
40
- # baz
41
- # end
40
+ # .each do
41
+ # baz
42
+ # end
42
43
  #
43
44
  # # good
44
45
  #
@@ -51,16 +52,17 @@ module RuboCop
51
52
  # # bad
52
53
  #
53
54
  # foo.bar
54
- # .each do
55
- # baz
56
- # end
55
+ # .each do
56
+ # baz
57
+ # end
57
58
  #
58
59
  # # good
59
60
  #
60
61
  # foo.bar
61
62
  # .each do
62
- # baz
63
+ # baz
63
64
  # end
65
+ #
64
66
  class BlockAlignment < Base
65
67
  include ConfigurableEnforcedStyle
66
68
  include RangeHelp
@@ -162,6 +162,7 @@ module RuboCop
162
162
  check_alignment([node.first_argument], indent)
163
163
  end
164
164
  alias on_csend on_send
165
+ alias on_super on_send
165
166
 
166
167
  private
167
168
 
@@ -148,7 +148,9 @@ module RuboCop
148
148
  check_indentation(in_pattern_node.loc.keyword, in_pattern_node.body)
149
149
  end
150
150
 
151
- check_indentation(case_match.in_pattern_branches.last.loc.keyword, case_match.else_branch)
151
+ else_branch = case_match.else_branch&.empty_else_type? ? nil : case_match.else_branch
152
+
153
+ check_indentation(case_match.in_pattern_branches.last.loc.keyword, else_branch)
152
154
  end
153
155
 
154
156
  def on_if(node, base = node)
@@ -341,7 +343,7 @@ module RuboCop
341
343
  end
342
344
 
343
345
  def skip_check?(base_loc, body_node)
344
- return true if ignored_line?(base_loc)
346
+ return true if allowed_line?(base_loc)
345
347
  return true unless body_node
346
348
 
347
349
  # Don't check if expression is on same line as "then" keyword, etc.
@@ -169,19 +169,22 @@ module RuboCop
169
169
 
170
170
  def qualifies_for_compact?(node, token, side: :right)
171
171
  if side == :right
172
- multi_dimensional_array?(node, token) && !next_to_bracket?(token)
172
+ multi_dimensional_array?(node, token) && token.space_before?
173
173
  else
174
- multi_dimensional_array?(node, token, side: :left) &&
175
- !next_to_bracket?(token, side: :left)
174
+ multi_dimensional_array?(node, token, side: :left) && token.space_after?
176
175
  end
177
176
  end
178
177
 
179
178
  def multi_dimensional_array?(node, token, side: :right)
180
- i = index_for(node, token)
179
+ offset = side == :right ? -1 : +1
180
+ i = index_for(node, token) + offset
181
+ # TODO: change this type check once
182
+ # https://github.com/rubocop/rubocop-ast/pull/240 is merged
183
+ i += offset while processed_source.tokens_within(node)[i].new_line?
181
184
  if side == :right
182
- processed_source.tokens_within(node)[i - 1].right_bracket?
185
+ processed_source.tokens_within(node)[i].right_bracket?
183
186
  else
184
- processed_source.tokens_within(node)[i + 1].left_array_bracket?
187
+ processed_source.tokens_within(node)[i].left_array_bracket?
185
188
  end
186
189
  end
187
190
 
@@ -200,12 +203,13 @@ module RuboCop
200
203
  end
201
204
 
202
205
  def compact_corrections(corrector, node, left, right)
203
- if qualifies_for_compact?(node, left, side: :left)
206
+ if multi_dimensional_array?(node, left, side: :left)
204
207
  compact(corrector, left, :right)
205
208
  elsif !left.space_after?
206
209
  corrector.insert_after(left.pos, ' ')
207
210
  end
208
- if qualifies_for_compact?(node, right)
211
+
212
+ if multi_dimensional_array?(node, right)
209
213
  compact(corrector, right, :left)
210
214
  elsif !right.space_before?
211
215
  corrector.insert_before(right.pos, ' ')
@@ -213,7 +217,7 @@ module RuboCop
213
217
  end
214
218
 
215
219
  def compact(corrector, bracket, side)
216
- range = side_space_range(range: bracket.pos, side: side)
220
+ range = side_space_range(range: bracket.pos, side: side, include_newlines: true)
217
221
  corrector.remove(range)
218
222
  end
219
223
  end
@@ -131,7 +131,7 @@ module RuboCop
131
131
  args_delimiter = node.arguments.loc.begin if node.block_type? # Can be ( | or nil.
132
132
 
133
133
  check_left_brace(inner, node.loc.begin, args_delimiter)
134
- check_right_brace(inner, node.loc.begin, node.loc.end, node.single_line?)
134
+ check_right_brace(node, inner, node.loc.begin, node.loc.end, node.single_line?)
135
135
  end
136
136
 
137
137
  def check_left_brace(inner, left_brace, args_delimiter)
@@ -142,14 +142,15 @@ module RuboCop
142
142
  end
143
143
  end
144
144
 
145
- def check_right_brace(inner, left_brace, right_brace, single_line)
145
+ def check_right_brace(node, inner, left_brace, right_brace, single_line)
146
146
  if single_line && /\S$/.match?(inner)
147
147
  no_space(right_brace.begin_pos, right_brace.end_pos, 'Space missing inside }.')
148
148
  else
149
+ column = node.loc.expression.column
149
150
  return if multiline_block?(left_brace, right_brace) &&
150
- aligned_braces?(left_brace, right_brace)
151
+ aligned_braces?(inner, right_brace, column)
151
152
 
152
- space_inside_right_brace(right_brace)
153
+ space_inside_right_brace(inner, right_brace, column)
153
154
  end
154
155
  end
155
156
 
@@ -157,8 +158,12 @@ module RuboCop
157
158
  left_brace.first_line != right_brace.first_line
158
159
  end
159
160
 
160
- def aligned_braces?(left_brace, right_brace)
161
- left_brace.first_line == right_brace.last_column
161
+ def aligned_braces?(inner, right_brace, column)
162
+ column == right_brace.column || column == inner_last_space_count(inner)
163
+ end
164
+
165
+ def inner_last_space_count(inner)
166
+ inner.split("\n").last.count(' ')
162
167
  end
163
168
 
164
169
  def no_space_inside_left_brace(left_brace, args_delimiter)
@@ -197,10 +202,21 @@ module RuboCop
197
202
  args_delimiter&.is?('|')
198
203
  end
199
204
 
200
- def space_inside_right_brace(right_brace)
205
+ def space_inside_right_brace(inner, right_brace, column)
201
206
  brace_with_space = range_with_surrounding_space(right_brace, side: :left)
202
- space(brace_with_space.begin_pos, brace_with_space.end_pos - 1,
203
- 'Space inside } detected.')
207
+ begin_pos = brace_with_space.begin_pos
208
+ end_pos = brace_with_space.end_pos - 1
209
+
210
+ if brace_with_space.source.match?(/\R/)
211
+ begin_pos = end_pos - (right_brace.column - column)
212
+ end
213
+
214
+ if inner.end_with?(']')
215
+ end_pos -= 1
216
+ begin_pos = end_pos - (inner_last_space_count(inner) - column)
217
+ end
218
+
219
+ space(begin_pos, end_pos, 'Space inside } detected.')
204
220
  end
205
221
 
206
222
  def no_space(begin_pos, end_pos, msg)
@@ -46,10 +46,13 @@ module RuboCop
46
46
  # # bad
47
47
  # foo = { }
48
48
  # bar = { }
49
+ # baz = {
50
+ # }
49
51
  #
50
52
  # # good
51
53
  # foo = {}
52
54
  # bar = {}
55
+ # baz = {}
53
56
  #
54
57
  # @example EnforcedStyleForEmptyBraces: space
55
58
  # # The `space` EnforcedStyleForEmptyBraces style enforces that
@@ -60,8 +63,9 @@ module RuboCop
60
63
  #
61
64
  # # good
62
65
  # foo = { }
63
- # foo = { }
64
- # foo = { }
66
+ # foo = { }
67
+ # foo = {
68
+ # }
65
69
  #
66
70
  class SpaceInsideHashLiteralBraces < Base
67
71
  include SurroundingSpace
@@ -77,6 +81,7 @@ module RuboCop
77
81
 
78
82
  check(tokens[0], tokens[1])
79
83
  check(tokens[-2], tokens[-1]) if tokens.size > 2
84
+ check_whitespace_only_hash(node) if enforce_no_space_style_for_empty_braces?
80
85
  end
81
86
 
82
87
  private
@@ -103,7 +108,7 @@ module RuboCop
103
108
  if is_same_braces && style == :compact
104
109
  false
105
110
  elsif is_empty_braces
106
- cop_config['EnforcedStyleForEmptyBraces'] != 'no_space'
111
+ !enforce_no_space_style_for_empty_braces?
107
112
  else
108
113
  style != :no_space
109
114
  end
@@ -175,6 +180,26 @@ module RuboCop
175
180
 
176
181
  range_between(begin_pos, range.end_pos - 1)
177
182
  end
183
+
184
+ def check_whitespace_only_hash(node)
185
+ range = range_inside_hash(node)
186
+ return unless range.source.match?(/\A\s+\z/m)
187
+
188
+ add_offense(
189
+ range,
190
+ message: format(MSG, problem: 'empty hash literal braces detected')
191
+ ) do |corrector|
192
+ corrector.remove(range)
193
+ end
194
+ end
195
+
196
+ def range_inside_hash(node)
197
+ range_between(node.location.begin.end_pos, node.location.end.begin_pos)
198
+ end
199
+
200
+ def enforce_no_space_style_for_empty_braces?
201
+ cop_config['EnforcedStyleForEmptyBraces'] == 'no_space'
202
+ end
178
203
  end
179
204
  end
180
205
  end
@@ -45,7 +45,7 @@ module RuboCop
45
45
  # # bad
46
46
  # expect { do_something }.to change { object.attribute }
47
47
  #
48
- # @example AllowedPatterns: [/change/]
48
+ # @example AllowedPatterns: ['change']
49
49
  #
50
50
  # # good
51
51
  # expect { do_something }.to change { object.attribute }
@@ -81,7 +81,7 @@ module RuboCop
81
81
  def allowed_method_pattern?(node)
82
82
  node.assignment? || node.operator_method? || node.method?(:[]) ||
83
83
  allowed_method?(node.last_argument.method_name) ||
84
- matches_allowed_pattern?(node.last_argument.method_name)
84
+ matches_allowed_pattern?(node.last_argument.send_node.source)
85
85
  end
86
86
 
87
87
  def message(send_node)
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for duplicated magic comments.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ #
12
+ # # encoding: ascii
13
+ # # encoding: ascii
14
+ #
15
+ # # good
16
+ #
17
+ # # encoding: ascii
18
+ #
19
+ # # bad
20
+ #
21
+ # # frozen_string_literal: true
22
+ # # frozen_string_literal: true
23
+ #
24
+ # # good
25
+ #
26
+ # # frozen_string_literal: true
27
+ #
28
+ class DuplicateMagicComment < Base
29
+ include FrozenStringLiteral
30
+ include RangeHelp
31
+ extend AutoCorrector
32
+
33
+ MSG = 'Duplicate magic comment detected.'
34
+
35
+ def on_new_investigation
36
+ return if processed_source.buffer.source.empty?
37
+
38
+ magic_comment_lines.each_value do |comment_lines|
39
+ next if comment_lines.count <= 1
40
+
41
+ comment_lines[1..].each do |comment_line|
42
+ range = processed_source.buffer.line_range(comment_line + 1)
43
+
44
+ register_offense(range)
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def magic_comment_lines
52
+ comment_lines = { encoding_magic_comments: [], frozen_string_literal_magic_comments: [] }
53
+
54
+ leading_magic_comments.each.with_index do |magic_comment, index|
55
+ if magic_comment.encoding_specified?
56
+ comment_lines[:encoding_magic_comments] << index
57
+ elsif magic_comment.frozen_string_literal_specified?
58
+ comment_lines[:frozen_string_literal_magic_comments] << index
59
+ end
60
+ end
61
+
62
+ comment_lines
63
+ end
64
+
65
+ def register_offense(range)
66
+ add_offense(range) do |corrector|
67
+ corrector.remove(range_by_whole_lines(range, include_final_newline: true))
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -133,7 +133,7 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def found_instance_method(node, name)
136
- return unless (scope = node.parent_module_name)
136
+ return found_sclass_method(node, name) unless (scope = node.parent_module_name)
137
137
 
138
138
  # Humanize the scope
139
139
  scope = scope.sub(
@@ -145,6 +145,16 @@ module RuboCop
145
145
  found_method(node, "#{scope}#{name}")
146
146
  end
147
147
 
148
+ def found_sclass_method(node, name)
149
+ singleton_ancestor = node.each_ancestor.find(&:sclass_type?)
150
+ return unless singleton_ancestor
151
+
152
+ singleton_receiver_node = singleton_ancestor.children[0]
153
+ return unless singleton_receiver_node.send_type?
154
+
155
+ found_method(node, "#{singleton_receiver_node.method_name}.#{name}")
156
+ end
157
+
148
158
  def found_method(node, method_name)
149
159
  if @definitions.key?(method_name)
150
160
  loc = case node.type
@@ -32,26 +32,40 @@ module RuboCop
32
32
  end
33
33
  end
34
34
 
35
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
35
36
  def each_repeated_character_class_element_loc(node)
36
37
  node.parsed_tree&.each_expression do |expr|
37
- next if expr.type != :set || expr.token == :intersection
38
+ next if skip_expression?(expr)
38
39
 
39
40
  seen = Set.new
41
+ enum = expr.expressions.to_enum
42
+ expression_count = expr.expressions.count
40
43
 
41
- expr.expressions.each do |child|
42
- next if within_interpolation?(node, child)
44
+ expression_count.times do |current_number|
45
+ current_child = enum.next
46
+ next if within_interpolation?(node, current_child)
43
47
 
44
- child_source = child.to_s
48
+ current_child_source = current_child.to_s
49
+ next_child = enum.peek if current_number + 1 < expression_count
45
50
 
46
- yield child.expression if seen.include?(child_source)
51
+ if seen.include?(current_child_source)
52
+ next if start_with_escaped_zero_number?(current_child_source, next_child.to_s)
47
53
 
48
- seen << child_source
54
+ yield current_child.expression
55
+ end
56
+
57
+ seen << current_child_source
49
58
  end
50
59
  end
51
60
  end
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
52
62
 
53
63
  private
54
64
 
65
+ def skip_expression?(expr)
66
+ expr.type != :set || expr.token == :intersection
67
+ end
68
+
55
69
  # Since we blank interpolations with a space for every char of the interpolation, we would
56
70
  # mark every space (except the first) as duplicate if we do not skip regexp_parser nodes
57
71
  # that are within an interpolation.
@@ -61,6 +75,11 @@ module RuboCop
61
75
  interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
62
76
  end
63
77
 
78
+ def start_with_escaped_zero_number?(current_child, next_child)
79
+ # Represents escaped code from `"\00"` (`"\u0000"`) to `"\07"` (`"\a"`).
80
+ current_child == '\\0' && next_child.match?(/[0-7]/)
81
+ end
82
+
64
83
  def interpolation_locs(node)
65
84
  @interpolation_locs ||= {}
66
85
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for duplicate `require`s and `require_relative`s.
6
+ # Checks for duplicate ``require``s and ``require_relative``s.
7
7
  #
8
8
  # @safety
9
9
  # This cop's autocorrection is unsafe because it may break the dependency order
@@ -85,7 +85,9 @@ module RuboCop
85
85
  private
86
86
 
87
87
  def body_or_allowed_comment_lines?(node)
88
- node.body || (cop_config['AllowComments'] && comment_lines?(node))
88
+ return true if node.body
89
+
90
+ cop_config['AllowComments'] && processed_source.contains_comment?(node.source_range)
89
91
  end
90
92
  end
91
93
  end