rubocop 0.55.0 → 0.56.0

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