rubocop 0.82.0 → 0.83.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +29 -4
  4. data/lib/rubocop.rb +2 -1
  5. data/lib/rubocop/ast/node/send_node.rb +4 -0
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/config.rb +5 -1
  8. data/lib/rubocop/config_loader.rb +15 -14
  9. data/lib/rubocop/config_loader_resolver.rb +27 -0
  10. data/lib/rubocop/config_validator.rb +2 -1
  11. data/lib/rubocop/cop/generator.rb +3 -2
  12. data/lib/rubocop/cop/layout/condition_position.rb +12 -2
  13. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +68 -0
  14. data/lib/rubocop/cop/layout/line_length.rb +4 -1
  15. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +13 -4
  16. data/lib/rubocop/cop/layout/space_around_operators.rb +18 -1
  17. data/lib/rubocop/cop/layout/trailing_whitespace.rb +2 -2
  18. data/lib/rubocop/cop/lint/ambiguous_operator.rb +38 -0
  19. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +14 -0
  20. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -5
  21. data/lib/rubocop/cop/lint/empty_when.rb +29 -6
  22. data/lib/rubocop/cop/lint/ensure_return.rb +18 -1
  23. data/lib/rubocop/cop/lint/literal_as_condition.rb +10 -13
  24. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +21 -9
  25. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +1 -6
  26. data/lib/rubocop/cop/lint/suppressed_exception.rb +0 -6
  27. data/lib/rubocop/cop/lint/useless_access_modifier.rb +12 -0
  28. data/lib/rubocop/cop/lint/useless_assignment.rb +3 -2
  29. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +5 -0
  30. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +10 -1
  31. data/lib/rubocop/cop/mixin/hash_transform_method.rb +8 -1
  32. data/lib/rubocop/cop/mixin/line_length_help.rb +2 -1
  33. data/lib/rubocop/cop/mixin/parser_diagnostic.rb +1 -1
  34. data/lib/rubocop/cop/mixin/statement_modifier.rb +7 -23
  35. data/lib/rubocop/cop/mixin/target_ruby_version.rb +5 -1
  36. data/lib/rubocop/cop/naming/method_name.rb +1 -5
  37. data/lib/rubocop/cop/style/case_equality.rb +1 -1
  38. data/lib/rubocop/cop/style/empty_method.rb +0 -4
  39. data/lib/rubocop/cop/style/guard_clause.rb +25 -2
  40. data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -0
  41. data/lib/rubocop/cop/style/lambda_call.rb +0 -20
  42. data/lib/rubocop/cop/style/multiline_when_then.rb +16 -1
  43. data/lib/rubocop/cop/style/optional_arguments.rb +1 -1
  44. data/lib/rubocop/cop/style/slicing_with_range.rb +39 -0
  45. data/lib/rubocop/cop/util.rb +24 -0
  46. data/lib/rubocop/cop/variable_force/assignment.rb +1 -0
  47. data/lib/rubocop/cop/variable_force/scope.rb +1 -0
  48. data/lib/rubocop/cop/variable_force/variable.rb +1 -0
  49. data/lib/rubocop/name_similarity.rb +12 -9
  50. data/lib/rubocop/options.rb +11 -4
  51. data/lib/rubocop/runner.rb +6 -1
  52. data/lib/rubocop/target_finder.rb +6 -4
  53. data/lib/rubocop/version.rb +1 -1
  54. metadata +4 -17
  55. data/lib/rubocop/string_util.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e04c6e897293df4ef1c7bf05a0c0077512e2cba797a7caee484b1c08a7300ad
4
- data.tar.gz: 92fbc98b5068e5c30bf83d058e3cfe774a1a65cc90608027ff8d6624c8c2910c
3
+ metadata.gz: 02ede8c9af1fb47b66ac79a0d93d577a5384f82f5076e1e64631ca761c4d3f6b
4
+ data.tar.gz: 2f22e7716a7068013b131636217a77189f2ca506e53c8613c407c18f8028600e
5
5
  SHA512:
6
- metadata.gz: cacf128617d233de65c2061d0300d345357840b151f65c78a2463fa23830115dcaa667cc2110ef39cacd14d957e626fc3fb4857443e40a1ab0fb485ce9cbfeed
7
- data.tar.gz: f7a095da673a5b32de33293c6056c580995d1b2ad913e2891c1740ab1ffb42896f1b0c69491eb75656907b60e4de6f6a58a119c8936cff26d1ed678bc1159e0c
6
+ metadata.gz: 936fdee12ea43542b66ab1cccc0514f1a7076dbd671386cccf376e61633b12e51c88feba3e652e77c925a2f5df4e0646bf568af453032bd2cb6eafdd9ea66d48
7
+ data.tar.gz: 23ec3628be52fd219206cd866c7e46757cdae9c8bd5c29334a7471c856f19becb03af7da2dd6f12a12c1f876f4eb900d10f9c8fdd7ad669c1bf2dea4cb333145
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/rubocop.svg)](https://badge.fury.io/rb/rubocop)
2
2
  [![CircleCI Status](https://circleci.com/gh/rubocop-hq/rubocop/tree/master.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop/tree/master)
3
- [![AppVeyor Status](https://ci.appveyor.com/api/projects/status/sj3ye7n5690d0nvg?svg=true)](https://ci.appveyor.com/project/bbatsov/rubocop)
3
+ [![Actions Status](https://github.com/rubocop-hq/rubocop/workflows/CI/badge.svg?branch=master)](https://github.com/rubocop-hq/rubocop/actions?query=workflow%3ACI)
4
4
  [![Coverage Status](https://api.codeclimate.com/v1/badges/ad6e76460499c8c99697/test_coverage)](https://codeclimate.com/github/bbatsov/rubocop)
5
5
  [![Code Climate](https://codeclimate.com/github/bbatsov/rubocop/badges/gpa.svg)](https://codeclimate.com/github/bbatsov/rubocop)
6
6
  [![Inline docs](https://inch-ci.org/github/bbatsov/rubocop.svg)](https://inch-ci.org/github/bbatsov/rubocop)
@@ -53,7 +53,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
53
53
  might want to use a conservative version lock in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 0.82.0', require: false
56
+ gem 'rubocop', '~> 0.83.0', require: false
57
57
  ```
58
58
 
59
59
  ## Quickstart
@@ -35,6 +35,7 @@ AllCops:
35
35
  - '**/*.watchr'
36
36
  - '**/.irbrc'
37
37
  - '**/.pryrc'
38
+ - '**/.simplecov'
38
39
  - '**/buildfile'
39
40
  - '**/Appraisals'
40
41
  - '**/Berksfile'
@@ -53,6 +54,7 @@ AllCops:
53
54
  - '**/Podfile'
54
55
  - '**/Puppetfile'
55
56
  - '**/Rakefile'
57
+ - '**/rakefile'
56
58
  - '**/Snapfile'
57
59
  - '**/Steepfile'
58
60
  - '**/Thorfile'
@@ -375,6 +377,7 @@ Layout/ConditionPosition:
375
377
  StyleGuide: '#same-line-condition'
376
378
  Enabled: true
377
379
  VersionAdded: '0.53'
380
+ VersionChanged: '0.83'
378
381
 
379
382
  Layout/DefEndAlignment:
380
383
  Description: 'Align ends corresponding to defs correctly.'
@@ -460,6 +463,12 @@ Layout/EmptyLinesAroundArguments:
460
463
  Enabled: true
461
464
  VersionAdded: '0.52'
462
465
 
466
+ Layout/EmptyLinesAroundAttributeAccessor:
467
+ Description: "Keep blank lines around attribute accessors."
468
+ StyleGuide: '#empty-lines-around-attribute-accessor'
469
+ Enabled: pending
470
+ VersionAdded: '0.83'
471
+
463
472
  Layout/EmptyLinesAroundBeginBody:
464
473
  Description: "Keeps track of empty lines around begin-end bodies."
465
474
  StyleGuide: '#empty-lines-around-bodies'
@@ -1295,8 +1304,8 @@ Layout/TrailingWhitespace:
1295
1304
  StyleGuide: '#no-trailing-whitespace'
1296
1305
  Enabled: true
1297
1306
  VersionAdded: '0.49'
1298
- VersionChanged: '0.55'
1299
- AllowInHeredoc: false
1307
+ VersionChanged: '0.83'
1308
+ AllowInHeredoc: true
1300
1309
 
1301
1310
  #################### Lint ##################################
1302
1311
  ### Warnings
@@ -1316,6 +1325,7 @@ Lint/AmbiguousOperator:
1316
1325
  StyleGuide: '#method-invocation-parens'
1317
1326
  Enabled: true
1318
1327
  VersionAdded: '0.17'
1328
+ VersionChanged: '0.83'
1319
1329
 
1320
1330
  Lint/AmbiguousRegexpLiteral:
1321
1331
  Description: >-
@@ -1323,6 +1333,7 @@ Lint/AmbiguousRegexpLiteral:
1323
1333
  a method invocation without parentheses.
1324
1334
  Enabled: true
1325
1335
  VersionAdded: '0.17'
1336
+ VersionChanged: '0.83'
1326
1337
 
1327
1338
  Lint/AssignmentInCondition:
1328
1339
  Description: "Don't use assignment in conditions."
@@ -1339,8 +1350,9 @@ Lint/BigDecimalNew:
1339
1350
  Lint/BooleanSymbol:
1340
1351
  Description: 'Check for `:true` and `:false` symbols.'
1341
1352
  Enabled: true
1353
+ Safe: false
1342
1354
  VersionAdded: '0.50'
1343
- VersionChanged: '0.81'
1355
+ VersionChanged: '0.83'
1344
1356
 
1345
1357
  Lint/CircularArgumentReference:
1346
1358
  Description: "Default values in optional keyword arguments and optional ordinal arguments should not refer back to the name of the argument."
@@ -1411,13 +1423,16 @@ Lint/EmptyInterpolation:
1411
1423
  Lint/EmptyWhen:
1412
1424
  Description: 'Checks for `when` branches with empty bodies.'
1413
1425
  Enabled: true
1426
+ AllowComments: true
1414
1427
  VersionAdded: '0.45'
1428
+ VersionChanged: '0.83'
1415
1429
 
1416
1430
  Lint/EnsureReturn:
1417
1431
  Description: 'Do not use return in an ensure block.'
1418
1432
  StyleGuide: '#no-return-ensure'
1419
1433
  Enabled: true
1420
1434
  VersionAdded: '0.9'
1435
+ VersionChanged: '0.83'
1421
1436
 
1422
1437
  Lint/ErbNewArguments:
1423
1438
  Description: 'Use `:trim_mode` and `:eoutvar` keyword arguments to `ERB.new`.'
@@ -1565,6 +1580,7 @@ Lint/ParenthesesAsGroupedExpression:
1565
1580
  StyleGuide: '#parens-no-spaces'
1566
1581
  Enabled: true
1567
1582
  VersionAdded: '0.12'
1583
+ VersionChanged: '0.83'
1568
1584
 
1569
1585
  Lint/PercentStringArray:
1570
1586
  Description: >-
@@ -1808,7 +1824,7 @@ Lint/UselessAccessModifier:
1808
1824
  Description: 'Checks for useless access modifiers.'
1809
1825
  Enabled: true
1810
1826
  VersionAdded: '0.20'
1811
- VersionChanged: '0.47'
1827
+ VersionChanged: '0.83'
1812
1828
  ContextCreatingMethods: []
1813
1829
  MethodCreatingMethods: []
1814
1830
 
@@ -2948,6 +2964,7 @@ Style/IfWithSemicolon:
2948
2964
  StyleGuide: '#no-semicolon-ifs'
2949
2965
  Enabled: true
2950
2966
  VersionAdded: '0.9'
2967
+ VersionChanged: '0.83'
2951
2968
 
2952
2969
  Style/ImplicitRuntimeError:
2953
2970
  Description: >-
@@ -3425,7 +3442,9 @@ Style/OptionalArguments:
3425
3442
  of the argument list.
3426
3443
  StyleGuide: '#optional-arguments'
3427
3444
  Enabled: true
3445
+ Safe: false
3428
3446
  VersionAdded: '0.33'
3447
+ VersionChanged: '0.83'
3429
3448
 
3430
3449
  Style/OrAssignment:
3431
3450
  Description: 'Recommend usage of double pipe equals (||=) where applicable.'
@@ -3730,6 +3749,12 @@ Style/SingleLineMethods:
3730
3749
  VersionChanged: '0.19'
3731
3750
  AllowIfMethodIsEmpty: true
3732
3751
 
3752
+ Style/SlicingWithRange:
3753
+ Description: 'Checks array slicing is done with endless ranges when suitable.'
3754
+ Enabled: pending
3755
+ VersionAdded: '0.83'
3756
+ Safe: false
3757
+
3733
3758
  Style/SpecialGlobalVars:
3734
3759
  Description: 'Avoid Perl-style global variables.'
3735
3760
  StyleGuide: '#no-cryptic-perlisms'
@@ -14,7 +14,6 @@ require_relative 'rubocop/core_ext/string'
14
14
  require_relative 'rubocop/path_util'
15
15
  require_relative 'rubocop/file_finder'
16
16
  require_relative 'rubocop/platform'
17
- require_relative 'rubocop/string_util'
18
17
  require_relative 'rubocop/name_similarity'
19
18
  require_relative 'rubocop/node_pattern'
20
19
  require_relative 'rubocop/string_interpreter'
@@ -213,6 +212,7 @@ require_relative 'rubocop/cop/layout/empty_line_after_magic_comment'
213
212
  require_relative 'rubocop/cop/layout/empty_line_between_defs'
214
213
  require_relative 'rubocop/cop/layout/empty_lines_around_access_modifier'
215
214
  require_relative 'rubocop/cop/layout/empty_lines_around_arguments'
215
+ require_relative 'rubocop/cop/layout/empty_lines_around_attribute_accessor'
216
216
  require_relative 'rubocop/cop/layout/empty_lines_around_begin_body'
217
217
  require_relative 'rubocop/cop/layout/empty_lines_around_block_body'
218
218
  require_relative 'rubocop/cop/layout/empty_lines_around_class_body'
@@ -542,6 +542,7 @@ require_relative 'rubocop/cop/style/send'
542
542
  require_relative 'rubocop/cop/style/signal_exception'
543
543
  require_relative 'rubocop/cop/style/single_line_block_params'
544
544
  require_relative 'rubocop/cop/style/single_line_methods'
545
+ require_relative 'rubocop/cop/style/slicing_with_range'
545
546
  require_relative 'rubocop/cop/style/special_global_vars'
546
547
  require_relative 'rubocop/cop/style/stabby_lambda_parentheses'
547
548
  require_relative 'rubocop/cop/style/stderr_puts'
@@ -8,6 +8,10 @@ module RuboCop
8
8
  class SendNode < Node
9
9
  include ParameterizedNode
10
10
  include MethodDispatchNode
11
+
12
+ def_node_matcher :attribute_accessor?, <<~PATTERN
13
+ (send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
14
+ PATTERN
11
15
  end
12
16
  end
13
17
  end
@@ -35,8 +35,8 @@ module RuboCop
35
35
  if @options[:init]
36
36
  run_command(:init)
37
37
  else
38
- validate_options_vs_config
39
38
  act_on_options
39
+ validate_options_vs_config
40
40
  apply_default_formatter
41
41
  execute_runners
42
42
  end
@@ -261,9 +261,13 @@ module RuboCop
261
261
 
262
262
  def enable_cop?(qualified_cop_name, cop_options)
263
263
  department = department_of(qualified_cop_name)
264
+ cop_enabled = cop_options.fetch('Enabled') do
265
+ !for_all_cops['DisabledByDefault']
266
+ end
267
+ return true if cop_enabled == 'override_department'
264
268
  return false if department && department['Enabled'] == false
265
269
 
266
- cop_options.fetch('Enabled') { !for_all_cops['DisabledByDefault'] }
270
+ cop_enabled
267
271
  end
268
272
 
269
273
  def department_of(qualified_cop_name)
@@ -36,7 +36,7 @@ module RuboCop
36
36
  FileFinder.root_level = nil
37
37
  end
38
38
 
39
- def load_file(file)
39
+ def load_file(file) # rubocop:disable Metrics/AbcSize
40
40
  path = File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
41
41
 
42
42
  hash = load_yaml_configuration(path)
@@ -46,6 +46,7 @@ module RuboCop
46
46
 
47
47
  add_missing_namespaces(path, hash)
48
48
 
49
+ resolver.override_department_setting_for_cops({}, hash)
49
50
  resolver.resolve_inheritance_from_gems(hash)
50
51
  resolver.resolve_inheritance(path, hash, file, debug?)
51
52
 
@@ -132,9 +133,9 @@ module RuboCop
132
133
  '`false` in your `.rubocop.yml` file:').yellow
133
134
 
134
135
  pending_cops.each do |cop|
135
- warn Rainbow(
136
- " - #{cop.name} (#{cop.metadata['VersionAdded']})"
137
- ).yellow
136
+ version = cop.metadata['VersionAdded'] || 'N/A'
137
+
138
+ warn Rainbow(" - #{cop.name} (#{version})").yellow
138
139
  end
139
140
 
140
141
  warn Rainbow('For more information: https://docs.rubocop.org/en/latest/versioning/').yellow
@@ -217,7 +218,10 @@ module RuboCop
217
218
  end
218
219
 
219
220
  def load_yaml_configuration(absolute_path)
220
- yaml_code = read_file(absolute_path)
221
+ file_contents = read_file(absolute_path)
222
+ yaml_code = Dir.chdir(File.dirname(absolute_path)) do
223
+ ERB.new(file_contents).result
224
+ end
221
225
  check_duplication(yaml_code, absolute_path)
222
226
  hash = yaml_safe_load(yaml_code, absolute_path) || {}
223
227
 
@@ -241,8 +245,7 @@ module RuboCop
241
245
  "#{smart_path}:#{line1}: " \
242
246
  "`#{value}` is concealed by line #{line2}"
243
247
  else
244
- "#{smart_path}: " \
245
- "`#{value}` is concealed by duplicate"
248
+ "#{smart_path}: `#{value}` is concealed by duplicate"
246
249
  end
247
250
  warn Rainbow(message).yellow
248
251
  end
@@ -263,13 +266,11 @@ module RuboCop
263
266
  SafeYAML.load(yaml_code, filename, whitelisted_tags: %w[!ruby/regexp])
264
267
  # Ruby 2.6+
265
268
  elsif Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0')
266
- YAML.safe_load(
267
- yaml_code,
268
- permitted_classes: [Regexp, Symbol],
269
- permitted_symbols: [],
270
- aliases: true,
271
- filename: filename
272
- )
269
+ YAML.safe_load(yaml_code,
270
+ permitted_classes: [Regexp, Symbol],
271
+ permitted_symbols: [],
272
+ aliases: true,
273
+ filename: filename)
273
274
  else
274
275
  YAML.safe_load(yaml_code, [Regexp, Symbol], [], true, filename)
275
276
  end
@@ -17,10 +17,12 @@ module RuboCop
17
17
  end
18
18
  end
19
19
 
20
+ # rubocop:disable Metrics/MethodLength
20
21
  def resolve_inheritance(path, hash, file, debug)
21
22
  inherited_files = Array(hash['inherit_from'])
22
23
  base_configs(path, inherited_files, file)
23
24
  .reverse.each_with_index do |base_config, index|
25
+ override_department_setting_for_cops(base_config, hash)
24
26
  base_config.each do |k, v|
25
27
  next unless v.is_a?(Hash)
26
28
 
@@ -34,6 +36,7 @@ module RuboCop
34
36
  end
35
37
  end
36
38
  end
39
+ # rubocop:enable Metrics/MethodLength
37
40
 
38
41
  def resolve_inheritance_from_gems(hash)
39
42
  gems = hash.delete('inherit_gem')
@@ -100,8 +103,32 @@ module RuboCop
100
103
  end
101
104
  # rubocop:enable Metrics/AbcSize
102
105
 
106
+ # An `Enabled: true` setting in user configuration for a cop overrides an
107
+ # `Enabled: false` setting for its department.
108
+ def override_department_setting_for_cops(base_hash, derived_hash)
109
+ derived_hash.each_key do |key|
110
+ next unless key =~ %r{(.*)/.*}
111
+
112
+ department = Regexp.last_match(1)
113
+ next unless disabled?(derived_hash, department) ||
114
+ disabled?(base_hash, department)
115
+
116
+ # The `override_department` setting for the `Enabled` parameter is an
117
+ # internal setting that's not documented in the manual. It will cause a
118
+ # cop to be enabled later, when logic surrounding enabled/disabled it
119
+ # run, even though its department is disabled.
120
+ if derived_hash[key]['Enabled']
121
+ derived_hash[key]['Enabled'] = 'override_department'
122
+ end
123
+ end
124
+ end
125
+
103
126
  private
104
127
 
128
+ def disabled?(hash, department)
129
+ hash[department] && hash[department]['Enabled'] == false
130
+ end
131
+
105
132
  def duplicate_setting?(base_hash, derived_hash, key, inherited_file)
106
133
  return false if inherited_file.nil? # Not inheritance resolving merge
107
134
  return false if inherited_file.start_with?('..') # Legitimate override
@@ -206,7 +206,8 @@ module RuboCop
206
206
  SafeAutoCorrect
207
207
  AutoCorrect].include?(key) && value.is_a?(String)
208
208
 
209
- next if key == 'Enabled' && value == 'pending'
209
+ next if key == 'Enabled' &&
210
+ %w[pending override_department].include?(value)
210
211
 
211
212
  raise ValidationError, msg_not_boolean(parent, key, value)
212
213
  end
@@ -132,11 +132,12 @@ module RuboCop
132
132
  ).inject
133
133
  end
134
134
 
135
- def inject_config(config_file_path: 'config/default.yml')
135
+ def inject_config(config_file_path: 'config/default.yml',
136
+ version_added: bump_minor_version)
136
137
  injector =
137
138
  ConfigurationInjector.new(configuration_file_path: config_file_path,
138
139
  badge: badge,
139
- version_added: bump_minor_version)
140
+ version_added: version_added)
140
141
 
141
142
  injector.inject do
142
143
  output.puts(format(CONFIGURATION_ADDED_MESSAGE,
@@ -23,6 +23,8 @@ module RuboCop
23
23
  # do_something
24
24
  # end
25
25
  class ConditionPosition < Cop
26
+ include RangeHelp
27
+
26
28
  MSG = 'Place the condition on the same line as `%<keyword>s`.'
27
29
 
28
30
  def on_if(node)
@@ -34,9 +36,17 @@ module RuboCop
34
36
  def on_while(node)
35
37
  check(node)
36
38
  end
39
+ alias on_until on_while
37
40
 
38
- def on_until(node)
39
- check(node)
41
+ def autocorrect(node)
42
+ lambda do |corrector|
43
+ range = range_by_whole_lines(
44
+ node.source_range, include_final_newline: true
45
+ )
46
+
47
+ corrector.insert_after(node.parent.loc.keyword, " #{node.source}")
48
+ corrector.remove(range)
49
+ end
40
50
  end
41
51
 
42
52
  private
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Layout
6
+ # Checks for a newline after attribute accessor.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # attr_accessor :foo
11
+ # def do_something
12
+ # end
13
+ #
14
+ # # good
15
+ # attr_accessor :foo
16
+ #
17
+ # def do_something
18
+ # end
19
+ #
20
+ # # good
21
+ # attr_accessor :foo
22
+ # attr_reader :bar
23
+ # attr_writer :baz
24
+ # attr :qux
25
+ #
26
+ # def do_something
27
+ # end
28
+ #
29
+ class EmptyLinesAroundAttributeAccessor < Cop
30
+ include RangeHelp
31
+
32
+ MSG = 'Add an empty line after attribute accessor.'
33
+
34
+ def on_send(node)
35
+ return unless node.attribute_accessor?
36
+ return if next_line_empty?(node.last_line)
37
+
38
+ next_line_node = next_line_node(node)
39
+ return if next_line_node.nil? || attribute_accessor?(next_line_node)
40
+
41
+ add_offense(node)
42
+ end
43
+
44
+ def autocorrect(node)
45
+ lambda do |corrector|
46
+ range = range_by_whole_lines(node.source_range)
47
+
48
+ corrector.insert_after(range, "\n")
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def next_line_empty?(line)
55
+ processed_source[line].blank?
56
+ end
57
+
58
+ def next_line_node(node)
59
+ node.parent.children[node.sibling_index + 1]
60
+ end
61
+
62
+ def attribute_accessor?(node)
63
+ node.send_type? && node.attribute_accessor?
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end