rubocop 0.55.0 → 0.56.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +45 -0
  4. data/config/disabled.yml +4 -4
  5. data/config/enabled.yml +32 -16
  6. data/lib/rubocop.rb +8 -2
  7. data/lib/rubocop/cli.rb +4 -0
  8. data/lib/rubocop/comment_config.rb +36 -9
  9. data/lib/rubocop/config.rb +8 -1
  10. data/lib/rubocop/cop/generator.rb +4 -3
  11. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +101 -29
  12. data/lib/rubocop/cop/{style → layout}/empty_line_after_guard_clause.rb +1 -1
  13. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +5 -5
  14. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +112 -2
  15. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +8 -4
  16. data/lib/rubocop/cop/lint/erb_new_arguments.rb +107 -0
  17. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -0
  18. data/lib/rubocop/cop/lint/nested_percent_literal.rb +0 -8
  19. data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
  20. data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
  21. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -0
  22. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +16 -3
  23. data/lib/rubocop/cop/lint/splat_keyword_arguments.rb +36 -0
  24. data/lib/rubocop/cop/lint/unneeded_cop_enable_directive.rb +20 -2
  25. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +95 -0
  26. data/lib/rubocop/cop/performance/unneeded_sort.rb +41 -6
  27. data/lib/rubocop/cop/rails/assert_not.rb +44 -0
  28. data/lib/rubocop/cop/rails/blank.rb +34 -28
  29. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -1
  30. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +12 -2
  31. data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -7
  32. data/lib/rubocop/cop/rails/present.rb +31 -25
  33. data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
  34. data/lib/rubocop/cop/rails/reversible_migration.rb +6 -4
  35. data/lib/rubocop/cop/rails/save_bang.rb +4 -1
  36. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -10
  37. data/lib/rubocop/cop/style/command_literal.rb +15 -3
  38. data/lib/rubocop/cop/style/comment_annotation.rb +6 -1
  39. data/lib/rubocop/cop/style/empty_method.rb +6 -3
  40. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +12 -2
  41. data/lib/rubocop/cop/style/method_missing_super.rb +34 -0
  42. data/lib/rubocop/cop/style/{method_missing.rb → missing_respond_to_missing.rb} +7 -29
  43. data/lib/rubocop/cop/style/parentheses_around_condition.rb +28 -2
  44. data/lib/rubocop/node_pattern.rb +1 -1
  45. data/lib/rubocop/processed_source.rb +12 -6
  46. data/lib/rubocop/result_cache.rb +9 -4
  47. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  48. data/lib/rubocop/runner.rb +8 -2
  49. data/lib/rubocop/target_finder.rb +40 -60
  50. data/lib/rubocop/version.rb +1 -1
  51. metadata +10 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b6193bafb35931ae47f1a4d40c8b9919f4f8df07
4
- data.tar.gz: 8b4c066f46a71fe4d35d84d0b1063f22130e2b4d
3
+ metadata.gz: fb9916f20b5502d5f82b3dda566fc49d305b165e
4
+ data.tar.gz: 0eb589fa94973ba3c4d06e147312982cb8cfe35c
5
5
  SHA512:
6
- metadata.gz: a619c39a295a5cfdc1a2d82a51391f8d995ea04e20a865eba6951056f57766be7df10718194782f715750f0d9007c6ee730688289acd277c371fc408e137c065
7
- data.tar.gz: 3ad640cddf0bedeaa238e9e13beef2da09f156c1bc957784c2779cc65076a7f142e0b85c113a6221e7792e491db4ebabeafea4c7424a92750523ddd55ecb4128
6
+ metadata.gz: 60768e70eb0d1066092c6d45b22be8d1f5bb154c5996296a32e48ba8aec05514bad6e5492d97849b70f0b357f99050237503a0da1ef33afdfb56c76e7f493126
7
+ data.tar.gz: 3d62fe67836732767da27fb00ab203103050466fd1064295a71109892ba10741ceaeea6f82a19c7866df6d77dbcec7af7d8cfe52bce3800457f5107f1d4ac3bb
data/README.md CHANGED
@@ -52,7 +52,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
52
52
  might want to use a conservative version locking in your `Gemfile`:
53
53
 
54
54
  ```rb
55
- gem 'rubocop', '~> 0.55.0', require: false
55
+ gem 'rubocop', '~> 0.56.0', require: false
56
56
  ```
57
57
 
58
58
  ## Quickstart
@@ -7,8 +7,15 @@ inherit_from:
7
7
 
8
8
  # Common configuration.
9
9
  AllCops:
10
+ RubyInterpreters:
11
+ - ruby
12
+ - macruby
13
+ - rake
14
+ - jruby
15
+ - rbx
10
16
  # Include common Ruby source files.
11
17
  Include:
18
+ - '**/*.rb'
12
19
  - '**/*.arb'
13
20
  - '**/*.axlsx'
14
21
  - '**/*.builder'
@@ -361,6 +368,9 @@ Layout/FirstParameterIndentation:
361
368
  # The first parameter should always be indented one step more than the
362
369
  # preceding line.
363
370
  - consistent
371
+ # The first parameter should always be indented one level relative to the
372
+ # parent that is receiving the parameter
373
+ - consistent_relative_to_receiver
364
374
  # The first parameter should normally be indented one step more than the
365
375
  # preceding line, but if it's a parameter for a method call that is itself
366
376
  # a parameter in a method call, then the inner parameter should be indented
@@ -1258,6 +1268,7 @@ Style/OptionHash:
1258
1268
  # Allow safe assignment in conditions.
1259
1269
  Style/ParenthesesAroundCondition:
1260
1270
  AllowSafeAssignment: true
1271
+ AllowInMultilineConditions: false
1261
1272
 
1262
1273
  Style/PercentLiteralDelimiters:
1263
1274
  # Specify the default preferred delimiter for all types with the 'default' key
@@ -1330,6 +1341,7 @@ Style/SafeNavigation:
1330
1341
  - blank?
1331
1342
  - presence
1332
1343
  - try
1344
+ - try!
1333
1345
 
1334
1346
  Style/Semicolon:
1335
1347
  # Allow `;` to separate several expressions on the same line.
@@ -1596,6 +1608,15 @@ Lint/SafeNavigationChain:
1596
1608
  - blank?
1597
1609
  - presence
1598
1610
  - try
1611
+ - try!
1612
+
1613
+ Lint/SafeNavigationConsistency:
1614
+ Whitelist:
1615
+ - present?
1616
+ - blank?
1617
+ - presence
1618
+ - try
1619
+ - try!
1599
1620
 
1600
1621
  # Checks for shadowed arguments
1601
1622
  Lint/ShadowedArgument:
@@ -1635,6 +1656,18 @@ Rails/ActionFilter:
1635
1656
  Include:
1636
1657
  - app/controllers/**/*.rb
1637
1658
 
1659
+ Rails/AssertNot:
1660
+ Include:
1661
+ - '**/test/**/*'
1662
+
1663
+ Rails/Blank:
1664
+ # Convert usages of `nil? || empty?` to `blank?`
1665
+ NilOrEmpty: true
1666
+ # Convert usages of `!present?` to `blank?`
1667
+ NotPresent: true
1668
+ # Convert usages of `unless present?` to `if blank?`
1669
+ UnlessPresent: true
1670
+
1638
1671
  Rails/CreateTableWithTimestamps:
1639
1672
  Include:
1640
1673
  - db/migrate/*.rb
@@ -1713,10 +1746,22 @@ Rails/Output:
1713
1746
  - db/**/*.rb
1714
1747
  - lib/**/*.rb
1715
1748
 
1749
+ Rails/Present:
1750
+ # Convert usages of `!nil? && !empty?` to `present?`
1751
+ NotNilAndNotEmpty: true
1752
+ # Convert usages of `!blank?` to `present?`
1753
+ NotBlank: true
1754
+ # Convert usages of `unless blank?` to `if present?`
1755
+ UnlessBlank: true
1756
+
1716
1757
  Rails/ReadWriteAttribute:
1717
1758
  Include:
1718
1759
  - app/models/**/*.rb
1719
1760
 
1761
+ Rails/RefuteMethods:
1762
+ Include:
1763
+ - '**/test/**/*'
1764
+
1720
1765
  Rails/RequestReferer:
1721
1766
  EnforcedStyle: referer
1722
1767
  SupportedStyles:
@@ -5,6 +5,10 @@ Layout/ClassStructure:
5
5
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-classes'
6
6
  Enabled: false
7
7
 
8
+ Layout/EmptyLineAfterGuardClause:
9
+ Description: 'Add empty line after guard clause.'
10
+ Enabled: false
11
+
8
12
  Layout/FirstArrayElementLineBreak:
9
13
  Description: >-
10
14
  Checks for a line break before the first element in a
@@ -68,10 +72,6 @@ Style/DocumentationMethod:
68
72
  - 'spec/**/*'
69
73
  - 'test/**/*'
70
74
 
71
- Style/EmptyLineAfterGuardClause:
72
- Description: 'Add empty line after guard clause.'
73
- Enabled: false
74
-
75
75
  Style/ImplicitRuntimeError:
76
76
  Description: >-
77
77
  Use `raise` or `fail` with an explicit exception class and
@@ -513,6 +513,10 @@ Lint/EnsureReturn:
513
513
  StyleGuide: '#no-return-ensure'
514
514
  Enabled: true
515
515
 
516
+ Lint/ErbNewArguments:
517
+ Description: 'Use `:trim_mode` and `:eoutvar` keyword arguments to `ERB.new`.'
518
+ Enabled: true
519
+
516
520
  Lint/FloatOutOfRange:
517
521
  Description: >-
518
522
  Catches floating-point literals too large or small for Ruby to
@@ -681,6 +685,10 @@ Lint/ShadowingOuterLocalVariable:
681
685
  for block arguments or block local variables.
682
686
  Enabled: true
683
687
 
688
+ Lint/SplatKeywordArguments:
689
+ Description: 'Checks for use of splat keyword arguments as a single Hash.'
690
+ Enabled: true
691
+
684
692
  Lint/StringConversionInInterpolation:
685
693
  Description: 'Checks for Object#to_s usage in string interpolation.'
686
694
  StyleGuide: '#no-to-s'
@@ -983,6 +991,11 @@ Performance/FlatMap:
983
991
  # This can be dangerous since `flat_map` will only flatten 1 level, and
984
992
  # `flatten` without any parameters can flatten multiple levels.
985
993
 
994
+ Performance/InefficientHashSearch:
995
+ Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`'
996
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
997
+ Enabled: true
998
+
986
999
  Performance/LstripRstrip:
987
1000
  Description: 'Use `strip` instead of `lstrip.rstrip`.'
988
1001
  Enabled: true
@@ -1101,15 +1114,13 @@ Rails/ApplicationRecord:
1101
1114
  Description: 'Check that models subclass ApplicationRecord.'
1102
1115
  Enabled: true
1103
1116
 
1117
+ Rails/AssertNot:
1118
+ Description: 'Use `assert_not` instead of `assert !`.'
1119
+ Enabled: true
1120
+
1104
1121
  Rails/Blank:
1105
- Description: 'Enforce using `blank?` and `present?`.'
1122
+ Description: 'Enforces use of `blank?`.'
1106
1123
  Enabled: true
1107
- # Convert checks for `nil` or `empty?` to `blank?`
1108
- NilOrEmpty: true
1109
- # Convert usages of not `present?` to `blank?`
1110
- NotPresent: true
1111
- # Convert usages of `unless` `present?` to `if` `blank?`
1112
- UnlessPresent: true
1113
1124
 
1114
1125
  Rails/CreateTableWithTimestamps:
1115
1126
  Description: >-
@@ -1216,14 +1227,8 @@ Rails/Presence:
1216
1227
  Enabled: true
1217
1228
 
1218
1229
  Rails/Present:
1219
- Description: 'Enforce using `blank?` and `present?`.'
1230
+ Description: 'Enforces use of `present?`.'
1220
1231
  Enabled: true
1221
- NotNilAndNotEmpty: true
1222
- # Convert checks for not `nil` and not `empty?` to `present?`
1223
- NotBlank: true
1224
- # Convert usages of not `blank?` to `present?`
1225
- UnlessBlank: true
1226
- # Convert usages of `unless` `blank?` to `if` `present?`
1227
1232
 
1228
1233
  Rails/ReadWriteAttribute:
1229
1234
  Description: >-
@@ -1236,6 +1241,10 @@ Rails/RedundantReceiverInWithOptions:
1236
1241
  Description: 'Checks for redundant receiver in `with_options`.'
1237
1242
  Enabled: true
1238
1243
 
1244
+ Rails/RefuteMethods:
1245
+ Description: 'Use `assert_not` methods instead of `refute` methods.'
1246
+ Enabled: true
1247
+
1239
1248
  Rails/RelativeDateConstant:
1240
1249
  Description: 'Do not assign relative date to constants.'
1241
1250
  Enabled: true
@@ -1636,8 +1645,8 @@ Style/MethodDefParentheses:
1636
1645
  StyleGuide: '#method-parens'
1637
1646
  Enabled: true
1638
1647
 
1639
- Style/MethodMissing:
1640
- Description: 'Avoid using `method_missing`.'
1648
+ Style/MethodMissingSuper:
1649
+ Description: Checks for `method_missing` to call `super`.
1641
1650
  StyleGuide: '#no-method-missing'
1642
1651
  Enabled: true
1643
1652
 
@@ -1647,6 +1656,13 @@ Style/MinMax:
1647
1656
  and `Enumerable#max` in conjunction.'
1648
1657
  Enabled: true
1649
1658
 
1659
+ Style/MissingRespondToMissing:
1660
+ Description: >-
1661
+ Checks if `method_missing` is implemented
1662
+ without implementing `respond_to_missing`.
1663
+ StyleGuide: '#no-method-missing'
1664
+ Enabled: true
1665
+
1650
1666
  Style/MixinGrouping:
1651
1667
  Description: 'Checks for grouping of mixins in `class` and `module` bodies.'
1652
1668
  StyleGuide: '#mixin-grouping'
@@ -180,6 +180,7 @@ require_relative 'rubocop/cop/layout/def_end_alignment'
180
180
  require_relative 'rubocop/cop/layout/dot_position'
181
181
  require_relative 'rubocop/cop/layout/else_alignment'
182
182
  require_relative 'rubocop/cop/layout/empty_comment'
183
+ require_relative 'rubocop/cop/layout/empty_line_after_guard_clause'
183
184
  require_relative 'rubocop/cop/layout/empty_line_after_magic_comment'
184
185
  require_relative 'rubocop/cop/layout/empty_line_between_defs'
185
186
  require_relative 'rubocop/cop/layout/empty_lines_around_access_modifier'
@@ -264,6 +265,7 @@ require_relative 'rubocop/cop/lint/empty_interpolation'
264
265
  require_relative 'rubocop/cop/lint/empty_when'
265
266
  require_relative 'rubocop/cop/lint/end_in_method'
266
267
  require_relative 'rubocop/cop/lint/ensure_return'
268
+ require_relative 'rubocop/cop/lint/erb_new_arguments'
267
269
  require_relative 'rubocop/cop/lint/float_out_of_range'
268
270
  require_relative 'rubocop/cop/lint/format_parameter_mismatch'
269
271
  require_relative 'rubocop/cop/lint/handle_exceptions'
@@ -299,6 +301,7 @@ require_relative 'rubocop/cop/lint/script_permission'
299
301
  require_relative 'rubocop/cop/lint/shadowed_argument'
300
302
  require_relative 'rubocop/cop/lint/shadowed_exception'
301
303
  require_relative 'rubocop/cop/lint/shadowing_outer_local_variable'
304
+ require_relative 'rubocop/cop/lint/splat_keyword_arguments'
302
305
  require_relative 'rubocop/cop/lint/string_conversion_in_interpolation'
303
306
  require_relative 'rubocop/cop/lint/syntax'
304
307
  require_relative 'rubocop/cop/lint/underscore_prefixed_variable_name'
@@ -356,6 +359,7 @@ require_relative 'rubocop/cop/performance/double_start_end_with'
356
359
  require_relative 'rubocop/cop/performance/end_with'
357
360
  require_relative 'rubocop/cop/performance/fixed_size'
358
361
  require_relative 'rubocop/cop/performance/flat_map'
362
+ require_relative 'rubocop/cop/performance/inefficient_hash_search'
359
363
  require_relative 'rubocop/cop/performance/lstrip_rstrip'
360
364
  require_relative 'rubocop/cop/performance/range_include'
361
365
  require_relative 'rubocop/cop/performance/redundant_block_call'
@@ -411,7 +415,6 @@ require_relative 'rubocop/cop/style/empty_block_parameter'
411
415
  require_relative 'rubocop/cop/style/empty_case_condition'
412
416
  require_relative 'rubocop/cop/style/empty_else'
413
417
  require_relative 'rubocop/cop/style/empty_lambda_parameter'
414
- require_relative 'rubocop/cop/style/empty_line_after_guard_clause'
415
418
  require_relative 'rubocop/cop/style/empty_literal'
416
419
  require_relative 'rubocop/cop/style/empty_method'
417
420
  require_relative 'rubocop/cop/style/encoding'
@@ -443,9 +446,10 @@ require_relative 'rubocop/cop/style/method_call_without_args_parentheses'
443
446
  require_relative 'rubocop/cop/style/method_call_with_args_parentheses'
444
447
  require_relative 'rubocop/cop/style/method_called_on_do_end_block'
445
448
  require_relative 'rubocop/cop/style/method_def_parentheses'
446
- require_relative 'rubocop/cop/style/method_missing'
449
+ require_relative 'rubocop/cop/style/method_missing_super'
447
450
  require_relative 'rubocop/cop/style/min_max'
448
451
  require_relative 'rubocop/cop/style/missing_else'
452
+ require_relative 'rubocop/cop/style/missing_respond_to_missing'
449
453
  require_relative 'rubocop/cop/style/mixin_grouping'
450
454
  require_relative 'rubocop/cop/style/mixin_usage'
451
455
  require_relative 'rubocop/cop/style/module_function'
@@ -538,6 +542,7 @@ require_relative 'rubocop/cop/rails/active_record_aliases'
538
542
  require_relative 'rubocop/cop/rails/active_support_aliases'
539
543
  require_relative 'rubocop/cop/rails/application_job'
540
544
  require_relative 'rubocop/cop/rails/application_record'
545
+ require_relative 'rubocop/cop/rails/assert_not'
541
546
  require_relative 'rubocop/cop/rails/blank'
542
547
  require_relative 'rubocop/cop/rails/create_table_with_timestamps'
543
548
  require_relative 'rubocop/cop/rails/date'
@@ -564,6 +569,7 @@ require_relative 'rubocop/cop/rails/presence'
564
569
  require_relative 'rubocop/cop/rails/present'
565
570
  require_relative 'rubocop/cop/rails/read_write_attribute'
566
571
  require_relative 'rubocop/cop/rails/redundant_receiver_in_with_options'
572
+ require_relative 'rubocop/cop/rails/refute_methods'
567
573
  require_relative 'rubocop/cop/rails/request_referer'
568
574
  require_relative 'rubocop/cop/rails/reversible_migration'
569
575
  require_relative 'rubocop/cop/rails/relative_date_constant'
@@ -50,6 +50,10 @@ module RuboCop
50
50
  rescue IncorrectCopNameError => e
51
51
  warn e.message
52
52
  STATUS_ERROR
53
+ rescue OptionParser::InvalidOption => e
54
+ warn e.message
55
+ warn 'For usage information, use --help'
56
+ STATUS_ERROR
53
57
  rescue StandardError, SyntaxError, LoadError => e
54
58
  warn e.message
55
59
  warn e.backtrace
@@ -43,17 +43,14 @@ module RuboCop
43
43
  def extra_enabled_comments_with_names(extras, names)
44
44
  each_directive do |comment, cop_names, disabled|
45
45
  next unless comment_only_line?(comment.loc.expression.line)
46
- cop_names.each do |name|
47
- names[name] ||= 0
48
- if disabled
49
- names[name] += 1
50
- elsif names[name] > 0
51
- names[name] -= 1
52
- else
53
- extras << [comment, name]
54
- end
46
+
47
+ if !disabled && enable_all?(comment)
48
+ handle_enable_all(names, extras, comment)
49
+ else
50
+ handle_switch(cop_names, names, disabled, extras, comment)
55
51
  end
56
52
  end
53
+
57
54
  extras
58
55
  end
59
56
 
@@ -169,5 +166,35 @@ module RuboCop
169
166
  non_comment_tokens.map(&:line).uniq
170
167
  end
171
168
  end
169
+
170
+ def enable_all?(comment)
171
+ _, cops = comment.text.match(COMMENT_DIRECTIVE_REGEXP).captures
172
+ cops == 'all'
173
+ end
174
+
175
+ def handle_enable_all(names, extras, comment)
176
+ enabled_cops = 0
177
+ names.each do |name, counter|
178
+ next unless counter > 0
179
+
180
+ names[name] -= 1
181
+ enabled_cops += 1
182
+ end
183
+
184
+ extras << [comment, 'all'] if enabled_cops.zero?
185
+ end
186
+
187
+ def handle_switch(cop_names, names, disabled, extras, comment)
188
+ cop_names.each do |name|
189
+ names[name] ||= 0
190
+ if disabled
191
+ names[name] += 1
192
+ elsif names[name] > 0
193
+ names[name] -= 1
194
+ else
195
+ extras << [comment, name]
196
+ end
197
+ end
198
+ end
172
199
  end
173
200
  end
@@ -108,7 +108,10 @@ module RuboCop
108
108
  'Performance/HashEachMethods' =>
109
109
  'The `Performance/HashEachMethods` cop has been removed ' \
110
110
  'since it no longer provides performance benefits in ' \
111
- 'modern rubies.'
111
+ 'modern rubies.',
112
+ 'Style/MethodMissing' =>
113
+ 'The `Style/MethodMissing` cop has been split into ' \
114
+ '`Style/MethodMissingSuper` and `Style/MissingRespondToMissing`.'
112
115
  }.freeze
113
116
 
114
117
  OBSOLETE_PARAMETERS = [
@@ -288,6 +291,10 @@ module RuboCop
288
291
  @to_s ||= @hash.to_s
289
292
  end
290
293
 
294
+ def signature
295
+ @signature ||= Digest::MD5.hexdigest(to_s)
296
+ end
297
+
291
298
  def make_excludes_absolute
292
299
  each_key do |key|
293
300
  validate_section_presence(key)
@@ -98,8 +98,9 @@ module RuboCop
98
98
  end
99
99
  SPEC
100
100
 
101
- def initialize(name, output: $stdout)
101
+ def initialize(name, github_user, output: $stdout)
102
102
  @badge = Badge.parse(name)
103
+ @github_user = github_user
103
104
  @output = output
104
105
  return if badge.qualified?
105
106
 
@@ -145,7 +146,7 @@ module RuboCop
145
146
  <<-TODO.strip_indent
146
147
  Do 3 steps:
147
148
  1. Add an entry to the "New features" section in CHANGELOG.md,
148
- e.g. "Add new `#{badge}` cop. ([@your_id][])"
149
+ e.g. "Add new `#{badge}` cop. ([@#{github_user}][])"
149
150
  2. Modify the description of #{badge} in config/enabled.yml
150
151
  3. Implement your new cop in the generated file!
151
152
  TODO
@@ -153,7 +154,7 @@ module RuboCop
153
154
 
154
155
  private
155
156
 
156
- attr_reader :badge, :output
157
+ attr_reader :badge, :github_user, :output
157
158
 
158
159
  def write_unless_file_exists(path, contents)
159
160
  if File.exist?(path)
@@ -9,28 +9,70 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  #
12
- # # good: when x is on its own line, indent this way
13
- # func(
14
- # x,
15
- # y
16
- # )
12
+ # # bad
13
+ # some_method(
14
+ # a,
15
+ # b
16
+ # )
17
+ #
18
+ # some_method(
19
+ # a, b
20
+ # )
17
21
  #
18
- # # good: when x follows opening parenthesis, align parentheses
19
- # a = b * (x +
20
- # y
21
- # )
22
+ # some_method(a, b, c
23
+ # )
22
24
  #
23
- # # bad
24
- # def func(
25
- # x,
26
- # y
25
+ # some_method(a,
26
+ # b,
27
+ # c
28
+ # )
29
+ #
30
+ # some_method(a,
31
+ # x: 1,
32
+ # y: 2
27
33
  # )
28
- # end
34
+ #
35
+ # # Scenario 1: When First Parameter Is On Its Own Line
36
+ #
37
+ # # good: when first param is on a new line, right paren is *always*
38
+ # # outdented by IndentationWidth
39
+ # some_method(
40
+ # a,
41
+ # b
42
+ # )
43
+ #
44
+ # # good
45
+ # some_method(
46
+ # a, b
47
+ # )
48
+ #
49
+ # # Scenario 2: When First Parameter Is On The Same Line
50
+ #
51
+ # # good: when all other params are also on the same line, outdent
52
+ # # right paren by IndentationWidth
53
+ # some_method(a, b, c
54
+ # )
55
+ #
56
+ # # good: when all other params are on multiple lines, but are lined
57
+ # # up, align right paren with left paren
58
+ # some_method(a,
59
+ # b,
60
+ # c
61
+ # )
62
+ #
63
+ # # good: when other params are not lined up on multiple lines, outdent
64
+ # # right paren by IndentationWidth
65
+ # some_method(a,
66
+ # x: 1,
67
+ # y: 2
68
+ # )
69
+ #
70
+ #
29
71
  class ClosingParenthesisIndentation < Cop
30
72
  include Alignment
31
73
 
32
- MSG_INDENT =
33
- 'Indent `)` the same as the start of the line where `(` is.'.freeze
74
+ MSG_INDENT = 'Indent `)` to column %<expected>d (not %<actual>d)'
75
+ .freeze
34
76
  MSG_ALIGN = 'Align `)` with `(`.'.freeze
35
77
 
36
78
  def on_send(node)
@@ -53,35 +95,65 @@ module RuboCop
53
95
  private
54
96
 
55
97
  def check(node, elements)
98
+ left_paren = node.loc.begin
56
99
  right_paren = node.loc.end
57
100
 
58
101
  return unless right_paren && begins_its_line?(right_paren)
59
102
 
60
- correct_column = expected_column(node, elements)
103
+ correct_column = expected_column(left_paren, elements)
104
+
61
105
  @column_delta = correct_column - right_paren.column
62
106
 
63
107
  return if @column_delta.zero?
64
108
 
65
- left_paren = node.loc.begin
66
- msg = correct_column == left_paren.column ? MSG_ALIGN : MSG_INDENT
109
+ add_offense(right_paren,
110
+ location: right_paren,
111
+ message: message(correct_column,
112
+ left_paren,
113
+ right_paren))
114
+ end
67
115
 
68
- add_offense(right_paren, location: right_paren, message: msg)
116
+ def expected_column(left_paren, elements)
117
+ if !line_break_after_left_paren?(left_paren, elements) &&
118
+ all_elements_aligned?(elements)
119
+ left_paren.column
120
+ else
121
+ source_indent = processed_source
122
+ .line_indentation(last_argument_line(elements))
123
+ new_indent = source_indent - indentation_width
124
+
125
+ new_indent < 0 ? 0 : new_indent
126
+ end
69
127
  end
70
128
 
71
- def expected_column(node, elements)
72
- left_paren = node.loc.begin
129
+ def all_elements_aligned?(elements)
130
+ elements
131
+ .map { |e| e.loc.column }
132
+ .uniq
133
+ .count == 1
134
+ end
73
135
 
74
- if node.send_type? && fixed_parameter_indentation? ||
75
- line_break_after_left_paren?(left_paren, elements)
76
- left_paren.source_line =~ /\S/
136
+ def last_argument_line(elements)
137
+ elements
138
+ .last
139
+ .loc
140
+ .first_line
141
+ end
142
+
143
+ def message(correct_column, left_paren, right_paren)
144
+ if correct_column == left_paren.column
145
+ MSG_ALIGN
77
146
  else
78
- left_paren.column
147
+ format(
148
+ MSG_INDENT,
149
+ expected: correct_column,
150
+ actual: right_paren.column
151
+ )
79
152
  end
80
153
  end
81
154
 
82
- def fixed_parameter_indentation?
83
- config.for_cop('Layout/AlignParameters')['EnforcedStyle'] ==
84
- 'with_fixed_indentation'
155
+ def indentation_width
156
+ @config.for_cop('IndentationWidth')['Width'] || 2
85
157
  end
86
158
 
87
159
  def line_break_after_left_paren?(left_paren, elements)