rubocop 1.53.1 → 1.57.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +34 -8
  4. data/config/obsoletion.yml +5 -0
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/config_finder.rb +2 -2
  8. data/lib/rubocop/config_obsoletion/parameter_rule.rb +9 -1
  9. data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
  10. data/lib/rubocop/cop/base.rb +1 -1
  11. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -0
  12. data/lib/rubocop/cop/bundler/duplicated_group.rb +127 -0
  13. data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -1
  14. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -4
  15. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -1
  16. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  17. data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -21
  18. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
  19. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
  20. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  21. data/lib/rubocop/cop/layout/dot_position.rb +1 -5
  22. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +42 -9
  23. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +26 -3
  24. data/lib/rubocop/cop/layout/end_alignment.rb +7 -1
  25. data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
  26. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  27. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  28. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +17 -9
  29. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  30. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
  31. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  32. data/lib/rubocop/cop/layout/redundant_line_break.rb +13 -3
  33. data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
  34. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  35. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +2 -2
  36. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -1
  37. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  38. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +5 -0
  39. data/lib/rubocop/cop/lint/debugger.rb +17 -4
  40. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  41. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  42. data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -1
  43. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -7
  44. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +10 -0
  45. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  46. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
  47. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
  48. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -1
  49. data/lib/rubocop/cop/lint/struct_new_override.rb +12 -12
  50. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  51. data/lib/rubocop/cop/lint/to_enum_arguments.rb +5 -3
  52. data/lib/rubocop/cop/lint/useless_assignment.rb +38 -12
  53. data/lib/rubocop/cop/lint/void.rb +32 -20
  54. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  55. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  56. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  57. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +32 -4
  58. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  59. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  60. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  61. data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
  62. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
  63. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
  64. data/lib/rubocop/cop/mixin/string_help.rb +4 -2
  65. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  66. data/lib/rubocop/cop/naming/file_name.rb +1 -1
  67. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +3 -1
  68. data/lib/rubocop/cop/style/alias.rb +9 -8
  69. data/lib/rubocop/cop/style/arguments_forwarding.rb +280 -63
  70. data/lib/rubocop/cop/style/array_intersect.rb +13 -5
  71. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  72. data/lib/rubocop/cop/style/class_equality_comparison.rb +7 -0
  73. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  74. data/lib/rubocop/cop/style/combinable_loops.rb +4 -2
  75. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
  76. data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
  77. data/lib/rubocop/cop/style/for.rb +1 -1
  78. data/lib/rubocop/cop/style/format_string.rb +24 -3
  79. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -1
  80. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  81. data/lib/rubocop/cop/style/hash_conversion.rb +10 -0
  82. data/lib/rubocop/cop/style/identical_conditional_branches.rb +25 -3
  83. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  84. data/lib/rubocop/cop/style/lambda.rb +3 -3
  85. data/lib/rubocop/cop/style/lambda_call.rb +5 -0
  86. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -1
  87. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  88. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  89. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  90. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  91. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  92. data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
  93. data/lib/rubocop/cop/style/redundant_argument.rb +6 -1
  94. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  95. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
  96. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  97. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  98. data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
  99. data/lib/rubocop/cop/style/redundant_parentheses.rb +41 -15
  100. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -1
  101. data/lib/rubocop/cop/style/redundant_return.rb +7 -2
  102. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +5 -0
  103. data/lib/rubocop/cop/style/return_nil.rb +6 -2
  104. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +23 -9
  105. data/lib/rubocop/cop/style/semicolon.rb +0 -3
  106. data/lib/rubocop/cop/style/single_argument_dig.rb +2 -1
  107. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  108. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  109. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +30 -5
  110. data/lib/rubocop/cop/style/symbol_array.rb +35 -15
  111. data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
  112. data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
  113. data/lib/rubocop/cop/utils/regexp_ranges.rb +26 -13
  114. data/lib/rubocop/cop/variable_force/assignment.rb +14 -5
  115. data/lib/rubocop/file_finder.rb +4 -7
  116. data/lib/rubocop/formatter/html_formatter.rb +4 -2
  117. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  118. data/lib/rubocop/lsp/routes.rb +41 -18
  119. data/lib/rubocop/lsp/runtime.rb +22 -2
  120. data/lib/rubocop/lsp/server.rb +10 -4
  121. data/lib/rubocop/magic_comment.rb +12 -10
  122. data/lib/rubocop/result_cache.rb +4 -0
  123. data/lib/rubocop/rspec/shared_contexts.rb +2 -3
  124. data/lib/rubocop/runner.rb +5 -3
  125. data/lib/rubocop/server/cache.rb +1 -0
  126. data/lib/rubocop/server/client_command/exec.rb +1 -1
  127. data/lib/rubocop/string_interpreter.rb +3 -3
  128. data/lib/rubocop/target_finder.rb +7 -3
  129. data/lib/rubocop/target_ruby.rb +9 -5
  130. data/lib/rubocop/version.rb +1 -1
  131. data/lib/rubocop.rb +2 -0
  132. metadata +16 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7b38b9b2471ab2c5972c061427da41ddf9820f91b21e7fa5d89330b2647eaa5
4
- data.tar.gz: 1a48aaee106f9e6c0459c8c5ef0a98dc39455bf06a68174d6b52ac2ea74abb0d
3
+ metadata.gz: adf1db90b03283f492e0e10f234d54d6efc215a8b182965087313e3954d533dd
4
+ data.tar.gz: fa84dfd9d61e4834df9287e0ba0619622f4b7eb9e229e8de1172bc1742c04df5
5
5
  SHA512:
6
- metadata.gz: 181bd1ac83ae045a457b3b5b73bf8e18e964f7413cabfa843d594a10cc7435403827e0b001ef4d5574c0065944e942adb908b0e4bfac8766486f2c58dd10e8e1
7
- data.tar.gz: b14fad843c6eb6ba3aa2e521133fe63da2abfed97fb79b1e45893c055c273180d1f0d0ed25cf53c53885d41859637c4469a2527617bb1c84125331a4eaf24fa3
6
+ metadata.gz: 44a683eed5e4b9d26caf4d0c775a6f4ba00960b6aaeee29c3f610320d1471326d650e8fc41ee2725af07b4e6a50bf17d442232134522709ebb2a46ec5b80bf40
7
+ data.tar.gz: d4f5dda0dbf7689c55b39de3922c2bd1a0c40aa62ac1046f06c98a19e7f2719cfea11babd480ba4e9b9ff2203d20422a51f096165d5919bf564e009c3a91c087
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.53', require: false
56
+ gem 'rubocop', '~> 1.57', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
@@ -67,6 +67,8 @@ $ cd my/cool/ruby/project
67
67
  $ rubocop
68
68
  ```
69
69
 
70
+ You can also use this magic in your favorite editor with RuboCop's [built-in LSP](https://docs.rubocop.org/rubocop/usage/lsp.html).
71
+
70
72
  ## Documentation
71
73
 
72
74
  You can read a lot more about RuboCop in its [official docs](https://docs.rubocop.org).
data/config/default.yml CHANGED
@@ -30,6 +30,7 @@ AllCops:
30
30
  - '**/*.rbx'
31
31
  - '**/*.ru'
32
32
  - '**/*.ruby'
33
+ - '**/*.schema'
33
34
  - '**/*.spec'
34
35
  - '**/*.thor'
35
36
  - '**/*.watchr'
@@ -55,6 +56,7 @@ AllCops:
55
56
  - '**/Puppetfile'
56
57
  - '**/Rakefile'
57
58
  - '**/rakefile'
59
+ - '**/Schemafile'
58
60
  - '**/Snapfile'
59
61
  - '**/Steepfile'
60
62
  - '**/Thorfile'
@@ -171,6 +173,16 @@ Bundler/DuplicatedGem:
171
173
  - '**/Gemfile'
172
174
  - '**/gems.rb'
173
175
 
176
+ Bundler/DuplicatedGroup:
177
+ Description: 'Checks for duplicate group entries in Gemfile.'
178
+ Enabled: true
179
+ Severity: warning
180
+ VersionAdded: '1.56'
181
+ Include:
182
+ - '**/*.gemfile'
183
+ - '**/Gemfile'
184
+ - '**/gems.rb'
185
+
174
186
  Bundler/GemComment:
175
187
  Description: 'Add a comment describing each gem.'
176
188
  Enabled: false
@@ -577,6 +589,8 @@ Layout/EmptyLineBetweenDefs:
577
589
  EmptyLineBetweenMethodDefs: true
578
590
  EmptyLineBetweenClassDefs: true
579
591
  EmptyLineBetweenModuleDefs: true
592
+ # `DefLikeMacros` takes the name of any macro that you want to treat like a def.
593
+ DefLikeMacros: []
580
594
  # `AllowAdjacentOneLineDefs` means that single line method definitions don't
581
595
  # need an empty line between them. `true` by default.
582
596
  AllowAdjacentOneLineDefs: true
@@ -2156,7 +2170,9 @@ Lint/RedundantRegexpQuantifiers:
2156
2170
  Lint/RedundantRequireStatement:
2157
2171
  Description: 'Checks for unnecessary `require` statement.'
2158
2172
  Enabled: true
2173
+ SafeAutoCorrect: false
2159
2174
  VersionAdded: '0.76'
2175
+ VersionChanged: '1.57'
2160
2176
 
2161
2177
  Lint/RedundantSafeNavigation:
2162
2178
  Description: 'Checks for redundant safe navigation calls.'
@@ -3068,6 +3084,7 @@ Style/ArgumentsForwarding:
3068
3084
  StyleGuide: '#arguments-forwarding'
3069
3085
  Enabled: pending
3070
3086
  AllowOnlyRestArgument: true
3087
+ UseAnonymousForwarding: true
3071
3088
  VersionAdded: '1.1'
3072
3089
 
3073
3090
  Style/ArrayCoercion:
@@ -3339,7 +3356,9 @@ Style/ClassEqualityComparison:
3339
3356
  Description: 'Enforces the use of `Object#instance_of?` instead of class comparison for equality.'
3340
3357
  StyleGuide: '#instance-of-vs-class-comparison'
3341
3358
  Enabled: true
3359
+ SafeAutoCorrect: false
3342
3360
  VersionAdded: '0.93'
3361
+ VersionChanged: '1.57'
3343
3362
  AllowedMethods:
3344
3363
  - ==
3345
3364
  - equal?
@@ -3394,6 +3413,7 @@ Style/CollectionMethods:
3394
3413
  PreferredMethods:
3395
3414
  collect: 'map'
3396
3415
  collect!: 'map!'
3416
+ collect_concat: 'flat_map'
3397
3417
  inject: 'reduce'
3398
3418
  detect: 'find'
3399
3419
  find_all: 'select'
@@ -3912,8 +3932,9 @@ Style/HashConversion:
3912
3932
  Description: 'Avoid Hash[] in favor of ary.to_h or literal hashes.'
3913
3933
  StyleGuide: '#avoid-hash-constructor'
3914
3934
  Enabled: pending
3935
+ SafeAutoCorrect: false
3915
3936
  VersionAdded: '1.10'
3916
- VersionChanged: '1.11'
3937
+ VersionChanged: '1.55'
3917
3938
  AllowSplatArgument: true
3918
3939
 
3919
3940
  Style/HashEachMethods:
@@ -4114,12 +4135,6 @@ Style/InvertibleUnlessCondition:
4114
4135
  :none?: :any?
4115
4136
  :even?: :odd?
4116
4137
  :odd?: :even?
4117
- # `ActiveSupport` defines some common inverse methods. They are listed below,
4118
- # and not enabled by default.
4119
- # :present?: :blank?
4120
- # :blank?: :present?
4121
- # :include?: :exclude?
4122
- # :exclude?: :include?
4123
4138
 
4124
4139
  Style/IpAddresses:
4125
4140
  Description: "Don't include literal IP addresses in code."
@@ -4806,12 +4821,16 @@ Style/RedundantArgument:
4806
4821
  Enabled: pending
4807
4822
  Safe: false
4808
4823
  VersionAdded: '1.4'
4809
- VersionChanged: '1.40'
4824
+ VersionChanged: '1.55'
4810
4825
  Methods:
4811
4826
  # Array#join
4812
4827
  join: ''
4813
4828
  # Array#sum
4814
4829
  sum: 0
4830
+ # Kernel.#exit
4831
+ exit: true
4832
+ # Kernel.#exit!
4833
+ exit!: false
4815
4834
  # String#split
4816
4835
  split: ' '
4817
4836
  # String#chomp
@@ -4905,7 +4924,9 @@ Style/RedundantFilterChain:
4905
4924
  Identifies usages of `any?`, `empty?`, `none?` or `one?` predicate methods chained to
4906
4925
  `select`/`filter`/`find_all` and change them to use predicate method instead.
4907
4926
  Enabled: pending
4927
+ SafeAutoCorrect: false
4908
4928
  VersionAdded: '1.52'
4929
+ VersionChanged: '1.57'
4909
4930
 
4910
4931
  Style/RedundantFreeze:
4911
4932
  Description: "Checks usages of Object#freeze on immutable objects."
@@ -5167,6 +5188,11 @@ Style/SingleLineBlockParams:
5167
5188
  - acc
5168
5189
  - elem
5169
5190
 
5191
+ Style/SingleLineDoEndBlock:
5192
+ Description: 'Checks for single-line `do`...`end` blocks.'
5193
+ Enabled: pending
5194
+ VersionAdded: '1.57'
5195
+
5170
5196
  Style/SingleLineMethods:
5171
5197
  Description: 'Avoid single-line methods.'
5172
5198
  StyleGuide: '#no-single-line-methods'
@@ -224,6 +224,11 @@ changed_parameters:
224
224
  - AllowedMethods
225
225
  - AllowedPatterns
226
226
  severity: warning
227
+ - cops: Style/ArgumentsForwarding
228
+ parameters: AllowOnlyRestArgument
229
+ reason: "`AllowOnlyRestArgument` has no effect with TargetRubyVersion >= 3.2."
230
+ severity: warning
231
+ minimum_ruby_version: 3.2
227
232
 
228
233
  # Enforced styles that have been removed or replaced
229
234
  changed_enforced_styles:
@@ -10,6 +10,7 @@ module RuboCop
10
10
 
11
11
  AUTO_GENERATED_FILE = '.rubocop_todo.yml'
12
12
  YAML_OPTIONAL_DOC_START = /\A---(\s+#|\s*\z)/.freeze
13
+ PLACEHOLDER = '###rubocop:inherit_here'
13
14
 
14
15
  PHASE_1 = 'Phase 1 of 2: run Layout/LineLength cop'
15
16
  PHASE_2 = 'Phase 2 of 2: run all cops'
@@ -125,15 +126,19 @@ module RuboCop
125
126
 
126
127
  def existing_configuration(config_file)
127
128
  File.read(config_file, encoding: Encoding::UTF_8)
128
- .sub(/^inherit_from: *[^\n]+/, '')
129
- .sub(/^inherit_from: *(\n *- *[^\n]+)+/, '')
129
+ .sub(/^inherit_from: *[^\n]+/, PLACEHOLDER)
130
+ .sub(/^inherit_from: *(\n *- *[^\n]+)+/, PLACEHOLDER)
130
131
  end
131
132
 
132
133
  def write_config_file(file_name, file_string, rubocop_yml_contents)
133
134
  lines = /\S/.match?(rubocop_yml_contents) ? rubocop_yml_contents.split("\n", -1) : []
134
- doc_start_index = lines.index { |line| YAML_OPTIONAL_DOC_START.match?(line) } || -1
135
- lines.insert(doc_start_index + 1, "inherit_from:#{file_string}\n")
136
- File.write(file_name, lines.join("\n"))
135
+ unless rubocop_yml_contents&.include?(PLACEHOLDER)
136
+ doc_start_index = lines.index { |line| YAML_OPTIONAL_DOC_START.match?(line) } || -1
137
+ lines.insert(doc_start_index + 1, PLACEHOLDER)
138
+ end
139
+ File.write(file_name, lines.join("\n")
140
+ .sub(/#{PLACEHOLDER}\n*/o, "inherit_from:#{file_string}\n\n")
141
+ .sub(/\n\n+\Z/, "\n"))
137
142
  end
138
143
 
139
144
  def relative_path_to_todo_from_options_config
data/lib/rubocop/cli.rb CHANGED
@@ -11,7 +11,7 @@ module RuboCop
11
11
  STATUS_ERROR = 2
12
12
  STATUS_INTERRUPTED = Signal.list['INT'] + 128
13
13
  DEFAULT_PARALLEL_OPTIONS = %i[
14
- color debug display_style_guide display_time display_only_fail_level_offenses
14
+ color config debug display_style_guide display_time display_only_fail_level_offenses
15
15
  display_only_failed except extra_details fail_level fix_layout format
16
16
  ignore_disable_comments lint only only_guide_cops require safe
17
17
  autocorrect safe_autocorrect autocorrect_all
@@ -46,14 +46,14 @@ module RuboCop
46
46
 
47
47
  file = File.join(Dir.home, DOTFILE)
48
48
 
49
- return file if File.exist?(file)
49
+ file if File.exist?(file)
50
50
  end
51
51
 
52
52
  def find_user_xdg_config
53
53
  xdg_config_home = expand_path(ENV.fetch('XDG_CONFIG_HOME', '~/.config'))
54
54
  xdg_config = File.join(xdg_config_home, 'rubocop', XDG_CONFIG)
55
55
 
56
- return xdg_config if File.exist?(xdg_config)
56
+ xdg_config if File.exist?(xdg_config)
57
57
  end
58
58
 
59
59
  def expand_path(path)
@@ -19,7 +19,7 @@ module RuboCop
19
19
  end
20
20
 
21
21
  def violated?
22
- config[cop]&.key?(parameter)
22
+ applies_to_current_ruby_version? && config[cop]&.key?(parameter)
23
23
  end
24
24
 
25
25
  def warning?
@@ -28,6 +28,14 @@ module RuboCop
28
28
 
29
29
  private
30
30
 
31
+ def applies_to_current_ruby_version?
32
+ minimum_ruby_version = metadata['minimum_ruby_version']
33
+
34
+ return true unless minimum_ruby_version
35
+
36
+ config.target_ruby_version >= minimum_ruby_version
37
+ end
38
+
31
39
  def alternative
32
40
  metadata['alternative']
33
41
  end
@@ -82,7 +82,9 @@ module RuboCop
82
82
  node.array_type? && node.percent_literal?
83
83
  end
84
84
 
85
- percent_array.map(&:source_range).find { |range| range.overlaps?(offense_range) }
85
+ percent_array.map(&:source_range).find do |range|
86
+ offense_range.begin_pos > range.begin_pos && range.overlaps?(offense_range)
87
+ end
86
88
  end
87
89
 
88
90
  def range_of_first_line(range)
@@ -53,7 +53,7 @@ module RuboCop
53
53
  # List of cops that should not try to autocorrect at the same
54
54
  # time as this cop
55
55
  #
56
- # @return [Array<RuboCop::Cop::Cop>]
56
+ # @return [Array<RuboCop::Cop::Base>]
57
57
  #
58
58
  # @api public
59
59
  def self.autocorrect_incompatible_with
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Bundler
6
6
  # A Gem's requirements should be listed only once in a Gemfile.
7
+ #
7
8
  # @example
8
9
  # # bad
9
10
  # gem 'rubocop'
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Bundler
6
+ # A Gem group, or a set of groups, should be listed only once in a Gemfile.
7
+ #
8
+ # For example, if the values of `source`, `git`, `platforms`, or `path`
9
+ # surrounding `group` are different, no offense will be registered:
10
+ #
11
+ # [source,ruby]
12
+ # -----
13
+ # platforms :ruby do
14
+ # group :default do
15
+ # gem 'openssl'
16
+ # end
17
+ # end
18
+ #
19
+ # platforms :jruby do
20
+ # group :default do
21
+ # gem 'jruby-openssl'
22
+ # end
23
+ # end
24
+ # -----
25
+ #
26
+ # @example
27
+ # # bad
28
+ # group :development do
29
+ # gem 'rubocop'
30
+ # end
31
+ #
32
+ # group :development do
33
+ # gem 'rubocop-rails'
34
+ # end
35
+ #
36
+ # # bad (same set of groups declared twice)
37
+ # group :development, :test do
38
+ # gem 'rubocop'
39
+ # end
40
+ #
41
+ # group :test, :development do
42
+ # gem 'rspec'
43
+ # end
44
+ #
45
+ # # good
46
+ # group :development do
47
+ # gem 'rubocop'
48
+ # end
49
+ #
50
+ # group :development, :test do
51
+ # gem 'rspec'
52
+ # end
53
+ #
54
+ # # good
55
+ # gem 'rubocop', groups: [:development, :test]
56
+ # gem 'rspec', groups: [:development, :test]
57
+ #
58
+ class DuplicatedGroup < Base
59
+ include RangeHelp
60
+
61
+ MSG = 'Gem group `%<group_name>s` already defined on line ' \
62
+ '%<line_of_first_occurrence>d of the Gemfile.'
63
+ SOURCE_BLOCK_NAMES = %i[source git platforms path].freeze
64
+
65
+ # @!method group_declarations(node)
66
+ def_node_search :group_declarations, '(send nil? :group ...)'
67
+
68
+ def on_new_investigation
69
+ return if processed_source.blank?
70
+
71
+ duplicated_group_nodes.each do |nodes|
72
+ nodes[1..].each do |node|
73
+ group_name = node.arguments.map(&:source).join(', ')
74
+
75
+ register_offense(node, group_name, nodes.first.first_line)
76
+ end
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def duplicated_group_nodes
83
+ group_declarations = group_declarations(processed_source.ast)
84
+ group_keys = group_declarations.group_by do |node|
85
+ source_key = find_source_key(node)
86
+ group_attributes = group_attributes(node).sort.join
87
+
88
+ "#{source_key}#{group_attributes}"
89
+ end
90
+
91
+ group_keys.values.select { |nodes| nodes.size > 1 }
92
+ end
93
+
94
+ def register_offense(node, group_name, line_of_first_occurrence)
95
+ line_range = node.loc.column...node.loc.last_column
96
+ offense_location = source_range(processed_source.buffer, node.first_line, line_range)
97
+ message = format(
98
+ MSG,
99
+ group_name: group_name,
100
+ line_of_first_occurrence: line_of_first_occurrence
101
+ )
102
+ add_offense(offense_location, message: message)
103
+ end
104
+
105
+ def find_source_key(node)
106
+ source_block = node.each_ancestor(:block).find do |block_node|
107
+ SOURCE_BLOCK_NAMES.include?(block_node.method_name)
108
+ end
109
+
110
+ return unless source_block
111
+
112
+ "#{source_block.method_name}#{source_block.send_node.first_argument&.source}"
113
+ end
114
+
115
+ def group_attributes(node)
116
+ node.arguments.map do |argument|
117
+ if argument.hash_type?
118
+ argument.pairs.map(&:source).sort.join(', ')
119
+ else
120
+ argument.respond_to?(:value) ? argument.value.to_s : argument.source
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -19,7 +19,15 @@ module RuboCop
19
19
  #
20
20
  # gem 'rspec'
21
21
  #
22
- # # good only if TreatCommentsAsGroupSeparators is true
22
+ # @example TreatCommentsAsGroupSeparators: true (default)
23
+ # # good
24
+ # # For code quality
25
+ # gem 'rubocop'
26
+ # # For tests
27
+ # gem 'rspec'
28
+ #
29
+ # @example TreatCommentsAsGroupSeparators: false
30
+ # # bad
23
31
  # # For code quality
24
32
  # gem 'rubocop'
25
33
  # # For tests
@@ -14,12 +14,15 @@ module RuboCop
14
14
  # Check for unparenthesized args' preceding and trailing whitespaces.
15
15
  remove_unparenthesized_whitespace(corrector)
16
16
 
17
- # Avoid correcting to `lambdado` by inserting whitespace
18
- # if none exists before or after the lambda arguments.
19
- insert_separating_space(corrector)
17
+ if block_node.block_type?
18
+ # Avoid correcting to `lambdado` by inserting whitespace
19
+ # if none exists before or after the lambda arguments.
20
+ insert_separating_space(corrector)
21
+
22
+ remove_arguments(corrector)
23
+ end
20
24
 
21
25
  replace_selector(corrector)
22
- remove_arguments(corrector)
23
26
 
24
27
  replace_delimiters(corrector)
25
28
 
@@ -45,7 +45,15 @@ module RuboCop
45
45
  #
46
46
  # spec.add_runtime_dependency 'rspec'
47
47
  #
48
- # # good only if TreatCommentsAsGroupSeparators is true
48
+ # @example TreatCommentsAsGroupSeparators: true (default)
49
+ # # good
50
+ # # For code quality
51
+ # spec.add_dependency 'rubocop'
52
+ # # For tests
53
+ # spec.add_dependency 'rspec'
54
+ #
55
+ # @example TreatCommentsAsGroupSeparators: false
56
+ # # bad
49
57
  # # For code quality
50
58
  # spec.add_dependency 'rubocop'
51
59
  # # For tests
@@ -67,7 +67,7 @@ module RuboCop
67
67
 
68
68
  def require_path
69
69
  path = source_path.relative_path_from(root_file_path.dirname)
70
- path.to_s.sub('.rb', '')
70
+ path.to_s.delete_suffix('.rb')
71
71
  end
72
72
  end
73
73
  end
@@ -26,9 +26,7 @@ module RuboCop
26
26
  # expect_no_offenses('...')
27
27
  # end
28
28
  class ExampleDescription < Base
29
- class << self
30
- attr_accessor :descriptions
31
- end
29
+ extend AutoCorrector
32
30
 
33
31
  MSG = 'Description does not match use of `%<method_name>s`.'
34
32
 
@@ -39,21 +37,31 @@ module RuboCop
39
37
  expect_no_corrections
40
38
  ].to_set.freeze
41
39
 
42
- EXPECT_NO_OFFENSES_INCORRECT_DESCRIPTIONS = [
43
- /^(adds|registers|reports|finds) (an? )?offense/,
44
- /^(flags|handles|works)\b/
45
- ].freeze
40
+ EXPECT_NO_OFFENSES_DESCRIPTION_MAPPING = {
41
+ /\A(adds|registers|reports|finds) (an? )?offense/ => 'does not register an offense',
42
+ /\A(flags|handles|works)\b/ => 'does not register'
43
+ }.freeze
44
+
45
+ EXPECT_OFFENSE_DESCRIPTION_MAPPING = {
46
+ /\A(does not|doesn't) (register|find|flag|report)/ => 'registers',
47
+ /\A(does not|doesn't) add (a|an|any )?offense/ => 'registers an offense',
48
+ /\Aaccepts\b/ => 'registers'
49
+ }.freeze
46
50
 
47
- EXPECT_OFFENSE_INCORRECT_DESCRIPTIONS = [
48
- /^(does not|doesn't) (register|find|flag|report)/,
49
- /^(does not|doesn't) add (a|an|any )?offense/
50
- ].freeze
51
+ EXPECT_NO_CORRECTIONS_DESCRIPTION_MAPPING = {
52
+ /\A(auto[- ]?)?correct/ => 'does not correct'
53
+ }.freeze
51
54
 
52
- EXPECT_NO_CORRECTIONS_INCORRECT_DESCRIPTIONS = [/^(auto[- ]?)?correct/].freeze
55
+ EXPECT_CORRECTION_DESCRIPTION_MAPPING = {
56
+ /\b(does not|doesn't) (auto[- ]?)?correct/ => 'autocorrects'
57
+ }.freeze
53
58
 
54
- EXPECT_CORRECTION_INCORRECT_DESCRIPTIONS = [
55
- /\b(does not|doesn't) (auto[- ]?)?correct/
56
- ].freeze
59
+ EXAMPLE_DESCRIPTION_MAPPING = {
60
+ expect_no_offenses: EXPECT_NO_OFFENSES_DESCRIPTION_MAPPING,
61
+ expect_offense: EXPECT_OFFENSE_DESCRIPTION_MAPPING,
62
+ expect_no_corrections: EXPECT_NO_CORRECTIONS_DESCRIPTION_MAPPING,
63
+ expect_correction: EXPECT_CORRECTION_DESCRIPTION_MAPPING
64
+ }.freeze
57
65
 
58
66
  # @!method offense_example?(node)
59
67
  def_node_matcher :offense_example?, <<~PATTERN
@@ -66,21 +74,34 @@ module RuboCop
66
74
 
67
75
  def on_send(node)
68
76
  parent = node.each_ancestor(:block).first
69
- return unless parent && (description = offense_example?(parent))
77
+ return unless parent && (current_description = offense_example?(parent))
70
78
 
71
79
  method_name = node.method_name
72
80
  message = format(MSG, method_name: method_name)
73
81
 
74
- regexp_group = self.class.const_get("#{method_name}_incorrect_descriptions".upcase)
75
- check_description(description, regexp_group, message)
82
+ description_map = EXAMPLE_DESCRIPTION_MAPPING[method_name]
83
+ check_description(current_description, description_map, message)
76
84
  end
77
85
 
78
86
  private
79
87
 
80
- def check_description(description, regexps, message)
81
- return unless regexps.any? { |regexp| regexp.match?(string_contents(description)) }
88
+ def check_description(current_description, description_map, message)
89
+ description_text = string_contents(current_description)
90
+ return unless (new_description = correct_description(description_text, description_map))
91
+
92
+ add_offense(current_description, message: message) do |corrector|
93
+ corrector.replace(current_description, "'#{new_description}'")
94
+ end
95
+ end
96
+
97
+ def correct_description(current_description, description_map)
98
+ description_map.each do |incorrect_description_pattern, preferred_description|
99
+ if incorrect_description_pattern.match?(current_description)
100
+ return current_description.gsub(incorrect_description_pattern, preferred_description)
101
+ end
102
+ end
82
103
 
83
- add_offense(description, message: message)
104
+ nil
84
105
  end
85
106
 
86
107
  def string_contents(node)
@@ -51,7 +51,9 @@ module RuboCop
51
51
 
52
52
  def extract_receiver(node)
53
53
  receiver = node.receiver
54
- receiver = receiver.receiver if receiver.method?(:loc) || receiver.method?(:source_range)
54
+ if receiver.send_type? && (receiver.method?(:loc) || receiver.method?(:source_range))
55
+ receiver = receiver.receiver
56
+ end
55
57
  receiver.source
56
58
  end
57
59
  end
@@ -14,6 +14,12 @@ module RuboCop
14
14
  # node.method_name
15
15
  #
16
16
  # # bad
17
+ # node.send_node.method?(:method_name)
18
+ #
19
+ # # good
20
+ # node.method?(:method_name)
21
+ #
22
+ # # bad
17
23
  # node.send_node.receiver
18
24
  #
19
25
  # # good
@@ -24,11 +30,14 @@ module RuboCop
24
30
  extend AutoCorrector
25
31
 
26
32
  MSG = 'Remove the redundant `send_node`.'
27
- RESTRICT_ON_SEND = %i[method_name receiver].freeze
33
+ RESTRICT_ON_SEND = %i[method_name method? receiver].freeze
28
34
 
29
35
  # @!method dispatch_method(node)
30
36
  def_node_matcher :dispatch_method, <<~PATTERN
31
- (send $(send _ :send_node) _)
37
+ {
38
+ (send $(send _ :send_node) {:method_name :receiver})
39
+ (send $(send _ :send_node) :method? _)
40
+ }
32
41
  PATTERN
33
42
 
34
43
  def on_send(node)
@@ -27,6 +27,8 @@ module RuboCop
27
27
  PATTERN
28
28
 
29
29
  def on_new_investigation
30
+ return if processed_source.blank?
31
+
30
32
  assertions_using_described_class_msg.each { |node| add_offense(node) }
31
33
  end
32
34
 
@@ -32,7 +32,7 @@ module RuboCop
32
32
  end
33
33
 
34
34
  def on_send(node)
35
- return unless node.dot? || ampersand_dot?(node)
35
+ return unless node.dot? || node.safe_navigation?
36
36
 
37
37
  return correct_style_detected if proper_dot_position?(node)
38
38
 
@@ -133,10 +133,6 @@ module RuboCop
133
133
  # l.(1) has no selector, so we use the opening parenthesis instead
134
134
  node.loc.selector || node.loc.begin
135
135
  end
136
-
137
- def ampersand_dot?(node)
138
- node.loc.respond_to?(:dot) && node.loc.dot && node.loc.dot.is?('&.')
139
- end
140
136
  end
141
137
  end
142
138
  end