rubocop 1.82.1 → 1.84.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +1 -1
  4. data/config/default.yml +44 -0
  5. data/lib/rubocop/cli/command/lsp.rb +1 -1
  6. data/lib/rubocop/cli.rb +2 -1
  7. data/lib/rubocop/comment_config.rb +1 -0
  8. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  9. data/lib/rubocop/cop/correctors/alignment_corrector.rb +20 -2
  10. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  11. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
  12. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  13. data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
  14. data/lib/rubocop/cop/layout/class_structure.rb +12 -5
  15. data/lib/rubocop/cop/layout/first_argument_indentation.rb +32 -1
  16. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
  17. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  18. data/lib/rubocop/cop/layout/heredoc_indentation.rb +34 -1
  19. data/lib/rubocop/cop/layout/indentation_width.rb +102 -9
  20. data/lib/rubocop/cop/layout/line_length.rb +5 -2
  21. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
  22. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
  23. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +8 -8
  24. data/lib/rubocop/cop/lint/duplicate_methods.rb +57 -5
  25. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  26. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
  27. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
  28. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  29. data/lib/rubocop/cop/lint/to_json.rb +12 -16
  30. data/lib/rubocop/cop/lint/useless_or.rb +1 -1
  31. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
  32. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +4 -4
  33. data/lib/rubocop/cop/naming/predicate_prefix.rb +11 -11
  34. data/lib/rubocop/cop/offense.rb +2 -1
  35. data/lib/rubocop/cop/security/json_load.rb +1 -1
  36. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -2
  37. data/lib/rubocop/cop/style/documentation.rb +6 -6
  38. data/lib/rubocop/cop/style/documentation_method.rb +8 -8
  39. data/lib/rubocop/cop/style/empty_class_definition.rb +144 -0
  40. data/lib/rubocop/cop/style/guard_clause.rb +7 -4
  41. data/lib/rubocop/cop/style/hash_lookup_method.rb +94 -0
  42. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
  43. data/lib/rubocop/cop/style/lambda_call.rb +8 -8
  44. data/lib/rubocop/cop/style/module_member_existence_check.rb +56 -13
  45. data/lib/rubocop/cop/style/negative_array_index.rb +218 -0
  46. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  47. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  48. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  49. data/lib/rubocop/cop/team.rb +3 -3
  50. data/lib/rubocop/cop/variable_force/branch.rb +28 -4
  51. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  52. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  53. data/lib/rubocop/formatter/tap_formatter.rb +5 -2
  54. data/lib/rubocop/remote_config.rb +5 -2
  55. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  56. data/lib/rubocop/rspec/support.rb +1 -0
  57. data/lib/rubocop/target_ruby.rb +3 -1
  58. data/lib/rubocop/version.rb +1 -1
  59. data/lib/rubocop.rb +4 -0
  60. metadata +9 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9dfdd0a2943006435a98725498ab85032b82d1cff547cd0bed007a250e56aca4
4
- data.tar.gz: c6a9e601a1e7858282d835dc00cd0f065ab3bc43de633988877dae6422f85f71
3
+ metadata.gz: dbf40b2db6dffc0c0f6685b18afbef40441dfc3650f0176a701ca9442748d12b
4
+ data.tar.gz: 4a40e7f7dbc92057286865804e49fa60b33391cd1947d87447fdaa18a1ad6dd2
5
5
  SHA512:
6
- metadata.gz: e19cc58194cf953813d3e3bf0d3bfa7d976f30127008ba84473b08cdcba188a4f73ac102baea188dfa463723b15fc768a3b7152306d5fcb5a5b7b7265e031ad2
7
- data.tar.gz: 26ed5927b0b375947c553a46434c6a58a63022c928b84b03ff1b44a8a74eee6c8cb29342262df28fcca223e194257d7e9fe7a06fb422d42e0805287380830743
6
+ metadata.gz: 0da21b5bc8b4db1ce03eefd577ee4e680354fb4b9bc8c4a1626b74dfe4d8ca4c31f4fcd629a635596db74d8d270686d3b353c3378ccd4d6d7b154129ea4af038
7
+ data.tar.gz: 1b470746489d85337a2ce6f9304d69b875c079a72f383fd8bf2f5154535cac98618341022fc5a5bf1d1fdc5ebe02f6be69d82f64d97c04d250215d41bb7f598b
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-25 Bozhidar Batsov
1
+ Copyright (c) 2012-26 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -251,5 +251,5 @@ RuboCop's release notes are available [here](https://github.com/rubocop/rubocop/
251
251
 
252
252
  ## Copyright
253
253
 
254
- Copyright (c) 2012-2025 Bozhidar Batsov. See [LICENSE.txt](LICENSE.txt) for
254
+ Copyright (c) 2012-2026 Bozhidar Batsov. See [LICENSE.txt](LICENSE.txt) for
255
255
  further details.
data/config/default.yml CHANGED
@@ -835,6 +835,7 @@ Layout/FirstArrayElementLineBreak:
835
835
  multi-line array.
836
836
  Enabled: false
837
837
  VersionAdded: '0.49'
838
+ AllowImplicitArrayLiterals: false
838
839
  AllowMultilineFinalElement: false
839
840
 
840
841
  Layout/FirstHashElementIndentation:
@@ -1037,6 +1038,15 @@ Layout/IndentationWidth:
1037
1038
  VersionAdded: '0.49'
1038
1039
  # Number of spaces for each indentation level.
1039
1040
  Width: 2
1041
+ # Block body indentation for method chain blocks.
1042
+ # The value `start_of_line` means that block bodies are indented
1043
+ # relative to the start of the line where the block starts.
1044
+ # The value `relative_to_receiver` means that block bodies are indented
1045
+ # relative to the method call position in the chain.
1046
+ EnforcedStyleAlignWith: start_of_line
1047
+ SupportedStylesAlignWith:
1048
+ - start_of_line
1049
+ - relative_to_receiver
1040
1050
  AllowedPatterns: []
1041
1051
 
1042
1052
  Layout/InitialIndentation:
@@ -3967,6 +3977,15 @@ Style/EmptyCaseCondition:
3967
3977
  Enabled: true
3968
3978
  VersionAdded: '0.40'
3969
3979
 
3980
+ Style/EmptyClassDefinition:
3981
+ Description: 'Enforces consistent style for empty class definitions.'
3982
+ Enabled: pending
3983
+ VersionAdded: '1.83'
3984
+ EnforcedStyle: class_definition
3985
+ SupportedStyles:
3986
+ - class_definition
3987
+ - class_new
3988
+
3970
3989
  Style/EmptyElse:
3971
3990
  Description: 'Avoid empty else-clauses.'
3972
3991
  Enabled: true
@@ -4318,6 +4337,16 @@ Style/HashLikeCase:
4318
4337
  # to trigger this cop
4319
4338
  MinBranchesCount: 3
4320
4339
 
4340
+ Style/HashLookupMethod:
4341
+ Description: 'Enforces the use of either `Hash#[]` or `Hash#fetch` for hash lookup.'
4342
+ Enabled: false
4343
+ Safe: false
4344
+ VersionAdded: '1.83'
4345
+ EnforcedStyle: brackets
4346
+ SupportedStyles:
4347
+ - brackets
4348
+ - fetch
4349
+
4321
4350
  Style/HashSlice:
4322
4351
  Description: >-
4323
4352
  Checks for usages of `Hash#reject`, `Hash#select`, and `Hash#filter` methods
@@ -4748,6 +4777,7 @@ Style/ModuleMemberExistenceCheck:
4748
4777
  Description: 'Checks for usage of `Module` methods returning arrays that can be replaced with equivalent predicates.'
4749
4778
  Enabled: pending
4750
4779
  VersionAdded: '1.82'
4780
+ AllowedMethods: []
4751
4781
 
4752
4782
  Style/MultilineBlockChain:
4753
4783
  Description: 'Avoid multi-line chains of blocks.'
@@ -4876,6 +4906,14 @@ Style/NegatedWhile:
4876
4906
  Enabled: true
4877
4907
  VersionAdded: '0.20'
4878
4908
 
4909
+ Style/NegativeArrayIndex:
4910
+ Description: >-
4911
+ Use negative array indices instead of calculating array length minus a value.
4912
+ Also handles range patterns with length calculations. Recognizes preserving methods
4913
+ and their combinations, allowing safe replacement when the receiver matches.
4914
+ Enabled: pending
4915
+ VersionAdded: '1.83'
4916
+
4879
4917
  Style/NestedFileDirname:
4880
4918
  Description: 'Checks for nested `File.dirname`.'
4881
4919
  Enabled: pending
@@ -5527,6 +5565,12 @@ Style/ReturnNilInPredicateMethodDefinition:
5527
5565
  VersionAdded: '1.53'
5528
5566
  VersionChanged: '1.67'
5529
5567
 
5568
+ Style/ReverseFind:
5569
+ Description: 'Use `array.rfind` instead of `array.reverse.find`.'
5570
+ Enabled: pending
5571
+ Safe: false
5572
+ VersionAdded: '1.83'
5573
+
5530
5574
  Style/SafeNavigation:
5531
5575
  Description: >-
5532
5576
  Transforms usages of a method call safeguarded by
@@ -9,7 +9,7 @@ module RuboCop
9
9
  self.command_name = :lsp
10
10
 
11
11
  def run
12
- # Load on demand, `languge-server-protocol` is heavy to require.
12
+ # Load on demand, `language-server-protocol` is heavy to require.
13
13
  require_relative '../../lsp/server'
14
14
  RuboCop::LSP::Server.new(@config_store).start
15
15
  end
data/lib/rubocop/cli.rb CHANGED
@@ -165,6 +165,8 @@ module RuboCop
165
165
  handle_editor_mode
166
166
 
167
167
  @config_store.apply_options!(@options)
168
+ # Set cache root after apply_options! to ensure force_default_config is applied first.
169
+ ConfigLoader.cache_root = ResultCache.cache_root(@config_store, @options[:cache_root])
168
170
 
169
171
  handle_exiting_options
170
172
 
@@ -183,7 +185,6 @@ module RuboCop
183
185
  ConfigLoader.enable_pending_cops = @options[:enable_pending_cops]
184
186
  ConfigLoader.ignore_parent_exclusion = @options[:ignore_parent_exclusion]
185
187
  ConfigLoader.ignore_unrecognized_cops = @options[:ignore_unrecognized_cops]
186
- ConfigLoader.cache_root = ResultCache.cache_root(@config_store, @options[:cache_root])
187
188
  end
188
189
 
189
190
  def set_options_to_pending_cops_reporter
@@ -70,6 +70,7 @@ module RuboCop
70
70
  def extra_enabled_comments_with_names(extras:, names:)
71
71
  each_directive do |directive|
72
72
  next unless comment_only_line?(directive.line_number)
73
+ next if directive.push? || directive.pop?
73
74
 
74
75
  if directive.enabled_all?
75
76
  handle_enable_all(directive, names, extras)
@@ -7,48 +7,48 @@ module RuboCop
7
7
  # ref, or tag) are either required or forbidden.
8
8
  #
9
9
  # @example EnforcedStyle: required (default)
10
- # # bad
11
- # gem 'rubocop'
10
+ # # bad
11
+ # gem 'rubocop'
12
12
  #
13
- # # good
14
- # gem 'rubocop', '~> 1.12'
13
+ # # good
14
+ # gem 'rubocop', '~> 1.12'
15
15
  #
16
- # # good
17
- # gem 'rubocop', '>= 1.10.0'
16
+ # # good
17
+ # gem 'rubocop', '>= 1.10.0'
18
18
  #
19
- # # good
20
- # gem 'rubocop', '>= 1.5.0', '< 1.10.0'
19
+ # # good
20
+ # gem 'rubocop', '>= 1.5.0', '< 1.10.0'
21
21
  #
22
- # # good
23
- # gem 'rubocop', branch: 'feature-branch'
22
+ # # good
23
+ # gem 'rubocop', branch: 'feature-branch'
24
24
  #
25
- # # good
26
- # gem 'rubocop', ref: '74b5bfbb2c4b6fd6cdbbc7254bd7084b36e0c85b'
25
+ # # good
26
+ # gem 'rubocop', ref: '74b5bfbb2c4b6fd6cdbbc7254bd7084b36e0c85b'
27
27
  #
28
- # # good
29
- # gem 'rubocop', tag: 'v1.17.0'
28
+ # # good
29
+ # gem 'rubocop', tag: 'v1.17.0'
30
30
  #
31
31
  # @example EnforcedStyle: forbidden
32
- # # good
33
- # gem 'rubocop'
32
+ # # good
33
+ # gem 'rubocop'
34
34
  #
35
- # # bad
36
- # gem 'rubocop', '~> 1.12'
35
+ # # bad
36
+ # gem 'rubocop', '~> 1.12'
37
37
  #
38
- # # bad
39
- # gem 'rubocop', '>= 1.10.0'
38
+ # # bad
39
+ # gem 'rubocop', '>= 1.10.0'
40
40
  #
41
- # # bad
42
- # gem 'rubocop', '>= 1.5.0', '< 1.10.0'
41
+ # # bad
42
+ # gem 'rubocop', '>= 1.5.0', '< 1.10.0'
43
43
  #
44
- # # bad
45
- # gem 'rubocop', branch: 'feature-branch'
44
+ # # bad
45
+ # gem 'rubocop', branch: 'feature-branch'
46
46
  #
47
- # # bad
48
- # gem 'rubocop', ref: '74b5bfbb2c4b6fd6cdbbc7254bd7084b36e0c85b'
47
+ # # bad
48
+ # gem 'rubocop', ref: '74b5bfbb2c4b6fd6cdbbc7254bd7084b36e0c85b'
49
49
  #
50
- # # bad
51
- # gem 'rubocop', tag: 'v1.17.0'
50
+ # # bad
51
+ # gem 'rubocop', tag: 'v1.17.0'
52
52
  #
53
53
  class GemVersion < Base
54
54
  include ConfigurableEnforcedStyle
@@ -16,6 +16,9 @@ module RuboCop
16
16
  return unless node
17
17
 
18
18
  @processed_source = processed_source
19
+ # Disable autocorrection for tabs as it requires special handling
20
+ return if using_tabs?
21
+
19
22
  expr = node.respond_to?(:loc) ? node.source_range : node
20
23
  return if block_comment_within?(expr)
21
24
 
@@ -30,11 +33,12 @@ module RuboCop
30
33
  @processed_source = processed_source
31
34
  whitespace = whitespace_range(node)
32
35
  column = alignment_column(align_to)
36
+ indentation = indentation_string(column)
33
37
 
34
38
  if whitespace.source.strip.empty?
35
- corrector.replace(whitespace, ' ' * column)
39
+ corrector.replace(whitespace, indentation)
36
40
  else
37
- corrector.insert_after(whitespace, "\n#{' ' * column}")
41
+ corrector.insert_after(whitespace, "\n#{indentation}")
38
42
  end
39
43
  end
40
44
 
@@ -120,6 +124,20 @@ module RuboCop
120
124
  align_to.column
121
125
  end
122
126
  end
127
+
128
+ def indentation_string(column)
129
+ if using_tabs?
130
+ "\t" * column
131
+ else
132
+ ' ' * column
133
+ end
134
+ end
135
+
136
+ def using_tabs?
137
+ config = processed_source.config
138
+ indentation_style = config.for_cop('Layout/IndentationStyle')['EnforcedStyle']
139
+ indentation_style == 'tabs'
140
+ end
123
141
  end
124
142
  end
125
143
  end
@@ -9,15 +9,15 @@ module RuboCop
9
9
  # the delimiter.
10
10
  #
11
11
  # @example
12
- # # bad
13
- # expect_offense(<<~CODE)
14
- # example_ruby_code
15
- # CODE
12
+ # # bad
13
+ # expect_offense(<<~CODE)
14
+ # example_ruby_code
15
+ # CODE
16
16
  #
17
- # # good
18
- # expect_offense(<<~RUBY)
19
- # example_ruby_code
20
- # RUBY
17
+ # # good
18
+ # expect_offense(<<~RUBY)
19
+ # example_ruby_code
20
+ # RUBY
21
21
  class ExampleHeredocDelimiter < Base
22
22
  extend AutoCorrector
23
23
 
@@ -8,16 +8,16 @@ module RuboCop
8
8
  # method.
9
9
  #
10
10
  # @example
11
- # # bad
12
- # def_node_matcher :foo?, <<~PATTERN
13
- # ...
14
- # PATTERN
11
+ # # bad
12
+ # def_node_matcher :foo?, <<~PATTERN
13
+ # ...
14
+ # PATTERN
15
15
  #
16
- # # good
17
- # # @!method foo?(node)
18
- # def_node_matcher :foo?, <<~PATTERN
19
- # ...
20
- # PATTERN
16
+ # # good
17
+ # # @!method foo?(node)
18
+ # def_node_matcher :foo?, <<~PATTERN
19
+ # ...
20
+ # PATTERN
21
21
  #
22
22
  class NodeMatcherDirective < Base
23
23
  extend AutoCorrector
@@ -7,11 +7,11 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  #
10
- # # bad
11
- # expect(cop.messages).to eq([described_class::MSG])
10
+ # # bad
11
+ # expect(cop.messages).to eq([described_class::MSG])
12
12
  #
13
- # # good
14
- # expect(cop.messages).to eq(['Do not write bad code like that.'])
13
+ # # good
14
+ # expect(cop.messages).to eq(['Do not write bad code like that.'])
15
15
  #
16
16
  class UselessMessageAssertion < Base
17
17
  MSG = 'Do not specify cop behavior using `described_class::MSG`.'
@@ -128,7 +128,9 @@ module RuboCop
128
128
  return if case_match_node.single_line?
129
129
  return if enforced_style_end? && end_and_last_conditional_same_line?(case_match_node)
130
130
 
131
- case_match_node.each_in_pattern { |in_pattern_node| check_when(in_pattern_node, 'in') }
131
+ case_match_node.in_pattern_branches.each do |in_pattern_node|
132
+ check_when(in_pattern_node, 'in')
133
+ end
132
134
  end
133
135
 
134
136
  private
@@ -223,7 +223,7 @@ module RuboCop
223
223
  # @param node to be analysed
224
224
  # @return String when the node type is a `:block` then
225
225
  # {classify} recursively with the first children
226
- # @return String when the node type is a `:send` then {find_category}
226
+ # @return String when the node type is a `:send` then {find_send_node_category}
227
227
  # by method name
228
228
  # @return String otherwise trying to {humanize_node} of the current node
229
229
  def classify(node)
@@ -233,9 +233,10 @@ module RuboCop
233
233
  when :block
234
234
  classify(node.send_node)
235
235
  when :send
236
- find_category(node)
236
+ find_send_node_category(node)
237
237
  else
238
- humanize_node(node)
238
+ name = humanize_node(node)
239
+ find_category(name) || name
239
240
  end.to_s
240
241
  end
241
242
 
@@ -244,9 +245,9 @@ module RuboCop
244
245
  # also its visibility.
245
246
  # @param node to be analysed.
246
247
  # @return [String] with the key category or the `method_name` as string
247
- def find_category(node)
248
+ def find_send_node_category(node)
248
249
  name = node.method_name.to_s
249
- category, = categories.find { |_, names| names.include?(name) }
250
+ category = find_category(name)
250
251
  key = category || name
251
252
  visibility_key =
252
253
  if node.def_modifier?
@@ -257,6 +258,12 @@ module RuboCop
257
258
  expected_order.include?(visibility_key) ? visibility_key : key
258
259
  end
259
260
 
261
+ def find_category(name)
262
+ name = name.to_s
263
+ category, = categories.find { |_, names| names.include?(name) }
264
+ category
265
+ end
266
+
260
267
  def walk_over_nested_class_definition(class_node)
261
268
  class_elements(class_node).each do |node|
262
269
  classification = classify(node)
@@ -172,7 +172,38 @@ module RuboCop
172
172
  end
173
173
 
174
174
  def autocorrect(corrector, node)
175
- AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
175
+ return unless node
176
+
177
+ send_node = node.parent
178
+ return unless send_node
179
+
180
+ top_level_send = find_top_level_send(send_node)
181
+ node_to_correct =
182
+ if top_level_send != send_node || should_correct_entire_method_call?(top_level_send)
183
+ top_level_send
184
+ else
185
+ node
186
+ end
187
+
188
+ AlignmentCorrector.correct(corrector, processed_source, node_to_correct, column_delta)
189
+ end
190
+
191
+ def find_top_level_send(send_node)
192
+ top_level_send = send_node
193
+ while top_level_send.parent&.send_type? &&
194
+ top_level_send.parent.receiver == top_level_send &&
195
+ top_level_send.parent.loc.dot
196
+ top_level_send = top_level_send.parent
197
+ end
198
+ top_level_send
199
+ end
200
+
201
+ def should_correct_entire_method_call?(send_node)
202
+ return false unless style == :special_for_inner_method_call_in_parentheses
203
+ return false unless special_inner_call_indentation?(send_node)
204
+
205
+ closing_paren = send_node.loc.end
206
+ closing_paren && begins_its_line?(closing_paren)
176
207
  end
177
208
 
178
209
  def bare_operator?(node)
@@ -20,6 +20,27 @@ module RuboCop
20
20
  # # good
21
21
  # [:a, :b]
22
22
  #
23
+ # @example AllowImplicitArrayLiterals: false (default)
24
+ #
25
+ # # bad
26
+ # a = b,
27
+ # c
28
+ #
29
+ # # good
30
+ # a =
31
+ # b,
32
+ # c
33
+ #
34
+ # @example AllowImplicitArrayLiterals: true
35
+ #
36
+ # # good
37
+ # a = b,
38
+ # c
39
+ #
40
+ # a =
41
+ # b,
42
+ # c
43
+ #
23
44
  # @example AllowMultilineFinalElement: false (default)
24
45
  #
25
46
  # # bad
@@ -48,6 +69,7 @@ module RuboCop
48
69
 
49
70
  def on_array(node)
50
71
  return if !node.loc.begin && !assignment_on_same_line?(node)
72
+ return if allow_implicit_array_brackets? && !node.bracketed?
51
73
 
52
74
  check_children_line_break(node, node.children, ignore_last: ignore_last_element?)
53
75
  end
@@ -59,6 +81,10 @@ module RuboCop
59
81
  /\s*=\s*$/.match?(source)
60
82
  end
61
83
 
84
+ def allow_implicit_array_brackets?
85
+ !!cop_config['AllowImplicitArrayLiterals']
86
+ end
87
+
62
88
  def ignore_last_element?
63
89
  !!cop_config['AllowMultilineFinalElement']
64
90
  end
@@ -8,40 +8,40 @@ module RuboCop
8
8
  #
9
9
  # @example
10
10
  #
11
- # # bad
12
- # { a: 1,
13
- # b: 2}
11
+ # # bad
12
+ # { a: 1,
13
+ # b: 2}
14
14
  #
15
- # # good
16
- # {
17
- # a: 1,
18
- # b: 2 }
15
+ # # good
16
+ # {
17
+ # a: 1,
18
+ # b: 2 }
19
19
  #
20
- # # good
21
- # {
22
- # a: 1, b: {
23
- # c: 3
24
- # }}
20
+ # # good
21
+ # {
22
+ # a: 1, b: {
23
+ # c: 3
24
+ # }}
25
25
  #
26
26
  # @example AllowMultilineFinalElement: false (default)
27
27
  #
28
- # # bad
29
- # { a: 1, b: {
30
- # c: 3
31
- # }}
28
+ # # bad
29
+ # { a: 1, b: {
30
+ # c: 3
31
+ # }}
32
32
  #
33
33
  # @example AllowMultilineFinalElement: true
34
34
  #
35
- # # bad
36
- # { a: 1,
37
- # b: {
38
- # c: 3
39
- # }}
35
+ # # bad
36
+ # { a: 1,
37
+ # b: {
38
+ # c: 3
39
+ # }}
40
40
  #
41
- # # good
42
- # { a: 1, b: {
43
- # c: 3
44
- # }}
41
+ # # good
42
+ # { a: 1, b: {
43
+ # c: 3
44
+ # }}
45
45
  #
46
46
  class FirstHashElementLineBreak < Base
47
47
  include FirstElementLineBreak
@@ -21,6 +21,23 @@ module RuboCop
21
21
  # something
22
22
  # RUBY
23
23
  #
24
+ # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
25
+ # # good
26
+ # <<-RUBY.squish
27
+ # something
28
+ # RUBY
29
+ #
30
+ # @example AllCops:ActiveSupportExtensionsEnabled: true
31
+ # # bad
32
+ # <<-RUBY.squish
33
+ # something
34
+ # RUBY
35
+ #
36
+ # # good
37
+ # <<~RUBY.squish
38
+ # something
39
+ # RUBY
40
+ #
24
41
  class HeredocIndentation < Base
25
42
  include Alignment
26
43
  include Heredoc
@@ -33,6 +50,11 @@ module RuboCop
33
50
  'heredoc by using `<<~` instead of `%<current_indent_type>s`.'
34
51
  WIDTH_MSG = 'Use %<indentation_width>d spaces for indentation in a heredoc.'
35
52
 
53
+ # @!method squish_method?(node)
54
+ def_node_matcher :squish_method?, <<~PATTERN
55
+ (send _ {:squish :squish!})
56
+ PATTERN
57
+
36
58
  def on_heredoc(node)
37
59
  body = heredoc_body(node)
38
60
  return if body.strip.empty?
@@ -44,7 +66,7 @@ module RuboCop
44
66
  expected_indent_level = base_indent_level(node) + configured_indentation_width
45
67
  return if expected_indent_level == body_indent_level
46
68
  else
47
- return unless body_indent_level.zero?
69
+ return unless body_indent_level.zero? || heredoc_squish?(node)
48
70
  end
49
71
 
50
72
  return if line_too_long?(node)
@@ -60,6 +82,8 @@ module RuboCop
60
82
  add_offense(node.loc.heredoc_body, message: message) do |corrector|
61
83
  if heredoc_indent_type == '~'
62
84
  adjust_squiggly(corrector, node)
85
+ elsif heredoc_squish?(node)
86
+ adjust_heredoc_squish(corrector, node)
63
87
  else
64
88
  adjust_minus(corrector, node)
65
89
  end
@@ -109,6 +133,11 @@ module RuboCop
109
133
  config.for_cop('Layout/LineLength')['AllowHeredoc']
110
134
  end
111
135
 
136
+ def adjust_heredoc_squish(corrector, node)
137
+ adjust_squiggly(corrector, node)
138
+ adjust_minus(corrector, node)
139
+ end
140
+
112
141
  def adjust_squiggly(corrector, node)
113
142
  corrector.replace(node.loc.heredoc_body, indented_body(node))
114
143
  corrector.replace(node.loc.heredoc_end, indented_end(node))
@@ -156,6 +185,10 @@ module RuboCop
156
185
  def heredoc_end(node)
157
186
  node.loc.heredoc_end.source
158
187
  end
188
+
189
+ def heredoc_squish?(node)
190
+ active_support_extensions_enabled? && squish_method?(node.parent)
191
+ end
159
192
  end
160
193
  end
161
194
  end