rubocop 0.57.2 → 0.58.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -9
  3. data/bin/setup +7 -0
  4. data/config/default.yml +18 -2
  5. data/config/disabled.yml +4 -0
  6. data/lib/rubocop.rb +1 -0
  7. data/lib/rubocop/ast/node.rb +5 -0
  8. data/lib/rubocop/ast/node/str_node.rb +2 -0
  9. data/lib/rubocop/cli.rb +4 -7
  10. data/lib/rubocop/config.rb +4 -4
  11. data/lib/rubocop/config_loader.rb +4 -8
  12. data/lib/rubocop/cop/corrector.rb +25 -0
  13. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +3 -7
  14. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  15. data/lib/rubocop/cop/layout/indentation_width.rb +9 -1
  16. data/lib/rubocop/cop/layout/leading_blank_lines.rb +1 -1
  17. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +28 -51
  18. data/lib/rubocop/cop/lint/redundant_with_object.rb +1 -1
  19. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -3
  20. data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +1 -1
  21. data/lib/rubocop/cop/lint/useless_access_modifier.rb +17 -4
  22. data/lib/rubocop/cop/metrics/line_length.rb +28 -6
  23. data/lib/rubocop/cop/mixin/check_assignment.rb +0 -2
  24. data/lib/rubocop/cop/mixin/statement_modifier.rb +6 -1
  25. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +79 -4
  26. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +9 -5
  27. data/lib/rubocop/cop/performance/range_include.rb +9 -3
  28. data/lib/rubocop/cop/performance/sample.rb +6 -4
  29. data/lib/rubocop/cop/rails/bulk_change_table.rb +11 -7
  30. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +3 -1
  31. data/lib/rubocop/cop/registry.rb +11 -2
  32. data/lib/rubocop/cop/style/encoding.rb +5 -0
  33. data/lib/rubocop/cop/style/end_block.rb +8 -0
  34. data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -1
  35. data/lib/rubocop/cop/style/ip_addresses.rb +76 -0
  36. data/lib/rubocop/cop/style/multiple_comparison.rb +16 -2
  37. data/lib/rubocop/cop/style/symbol_proc.rb +4 -2
  38. data/lib/rubocop/cop/style/unneeded_condition.rb +19 -2
  39. data/lib/rubocop/formatter/disabled_config_formatter.rb +3 -3
  40. data/lib/rubocop/options.rb +20 -12
  41. data/lib/rubocop/processed_source.rb +2 -5
  42. data/lib/rubocop/rspec/cop_helper.rb +0 -4
  43. data/lib/rubocop/rspec/shared_contexts.rb +0 -4
  44. data/lib/rubocop/rspec/shared_examples.rb +0 -23
  45. data/lib/rubocop/version.rb +1 -1
  46. metadata +7 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '059456c464bec007ceecf522ade27bd7cf5e7c5a'
4
- data.tar.gz: cfe7d827a44e1a84835870f38d25c4f9b13a4a00
3
+ metadata.gz: f3841f2f7a2e69362f57238be2722eb4f06bccf8
4
+ data.tar.gz: '0193123e23f57b0cea3701b8f59c90c2ea6b0658'
5
5
  SHA512:
6
- metadata.gz: bdd27ccbc33113527d0d6bfbdc83cc48cf83d1a00b1162328b25fa67b062b7d13330bb5011628aab74dacbcb690610f26e4e209361c1565c4ff9341c4e32cb48
7
- data.tar.gz: '092c870e83c677788d8d314a1869a84a2a505090fc5c703f10145d1ad513231087d539a497050d985e483cf869a46b8fb96424b3465933796683972f9b02928a'
6
+ metadata.gz: 59d32b347c6000e8b1b23981d00cde8017bee3870e941fd72482d415de3966c3928c11b566489ba55e22545d8243e82008df7c5ba4c69e788a75c878df2d13dd
7
+ data.tar.gz: 85337f25210448131a7bc0be46a654fd71db0590c5269a328208fbb874e82b79647150b09d64a2a1178bd7d322c013642bd1ecb59ba17516c276a1e31461916d
data/README.md CHANGED
@@ -1,9 +1,11 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/rubocop.svg)](http://badge.fury.io/rb/rubocop)
2
2
  [![Travis Status](https://travis-ci.org/rubocop-hq/rubocop.svg?branch=master)](https://travis-ci.org/rubocop-hq/rubocop)
3
+ [![CircleCI Status](https://circleci.com/gh/rubocop-hq/rubocop/tree/master.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop/tree/master)
3
4
  [![AppVeyor Status](https://ci.appveyor.com/api/projects/status/sj3ye7n5690d0nvg?svg=true)](https://ci.appveyor.com/project/bbatsov/rubocop)
4
5
  [![Coverage Status](https://img.shields.io/codeclimate/coverage/github/bbatsov/rubocop.svg)](https://codeclimate.com/github/bbatsov/rubocop)
5
6
  [![Code Climate](https://codeclimate.com/github/bbatsov/rubocop/badges/gpa.svg)](https://codeclimate.com/github/bbatsov/rubocop)
6
7
  [![Inline docs](http://inch-ci.org/github/bbatsov/rubocop.svg)](http://inch-ci.org/github/bbatsov/rubocop)
8
+ [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=rubocop&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=rubocop&package-manager=bundler&version-scheme=semver)
7
9
 
8
10
  [![Patreon](https://img.shields.io/badge/patreon-donate-orange.svg)](https://www.patreon.com/bbatsov)
9
11
  [![Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/bbatsov/donate)
@@ -11,7 +13,7 @@
11
13
  [![OpenCollective](https://opencollective.com/rubocop/sponsors/badge.svg)](#open-collective-sponsors)
12
14
 
13
15
  <p align="center">
14
- <img src="https://raw.githubusercontent.com/bbatsov/rubocop/master/logo/rubo-logo-horizontal.png" alt="RuboCop Logo"/>
16
+ <img src="https://raw.githubusercontent.com/rubocop-hq/rubocop/master/logo/rubo-logo-horizontal.png" alt="RuboCop Logo"/>
15
17
  </p>
16
18
 
17
19
  > Role models are important. <br/>
@@ -19,10 +21,10 @@
19
21
 
20
22
  **RuboCop** is a Ruby static code analyzer. Out of the box it will
21
23
  enforce many of the guidelines outlined in the community
22
- [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide).
24
+ [Ruby Style Guide](https://github.com/rubocop-hq/ruby-style-guide).
23
25
 
24
26
  Most aspects of its behavior can be tweaked via various
25
- [configuration options](https://github.com/bbatsov/rubocop/blob/master/config/default.yml).
27
+ [configuration options](https://github.com/rubocop-hq/rubocop/blob/master/config/default.yml).
26
28
 
27
29
  Apart from reporting problems in your code, RuboCop can also
28
30
  automatically fix some of the problems for you.
@@ -51,7 +53,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
51
53
  might want to use a conservative version locking in your `Gemfile`:
52
54
 
53
55
  ```rb
54
- gem 'rubocop', '~> 0.57.2', require: false
56
+ gem 'rubocop', '~> 0.58.0', require: false
55
57
  ```
56
58
 
57
59
  ## Quickstart
@@ -65,13 +67,13 @@ $ rubocop
65
67
 
66
68
  ## Official manual
67
69
 
68
- You can read a ton more about RuboCop in its [official manual](http://rubocop.readthedocs.io).
70
+ You can read a ton more about RuboCop in its [official manual](http://docs.rubocop.org).
69
71
 
70
72
  ## Compatibility
71
73
 
72
74
  RuboCop supports the following Ruby implementations:
73
75
 
74
- * MRI 2.1+
76
+ * MRI 2.2+
75
77
  * JRuby 9.0+
76
78
 
77
79
  The Rails cops support the following versions:
@@ -82,24 +84,25 @@ The Rails cops support the following versions:
82
84
 
83
85
  Here's a list of RuboCop's core developers:
84
86
 
85
- * [Bozhidar Batsov](https://github.com/bbatsov)
87
+ * [Bozhidar Batsov](https://github.com/bbatsov) (author & head maintainer)
86
88
  * [Jonas Arvidsson](https://github.com/jonas054)
87
89
  * [Yuji Nakayama](https://github.com/yujinakayama)
88
90
  * [Evgeni Dzhelyov](https://github.com/edzhelyov) (retired)
89
91
  * [Ted Johansson](https://github.com/drenmi)
90
92
  * [Masataka Kuwabara](https://github.com/pocke)
93
+ * [Koichi Ito](https://github.com/koic)
91
94
 
92
95
  ## Logo
93
96
 
94
97
  RuboCop's logo was created by [Dimiter Petrov](https://www.chadomoto.com/). You can find the logo in various
95
- formats [here](https://github.com/bbatsov/rubocop/tree/master/logo).
98
+ formats [here](https://github.com/rubocop-hq/rubocop/tree/master/logo).
96
99
 
97
100
  The logo is licensed under a
98
101
  [Creative Commons Attribution-NonCommercial 4.0 International License](http://creativecommons.org/licenses/by-nc/4.0/deed.en_GB).
99
102
 
100
103
  ## Contributors
101
104
 
102
- Here's a [list](https://github.com/bbatsov/rubocop/graphs/contributors) of
105
+ Here's a [list](https://github.com/rubocop-hq/rubocop/graphs/contributors) of
103
106
  all the people who have contributed to the development of RuboCop.
104
107
 
105
108
  I'm extremely grateful to each and every one of them!
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+ IFS=$'\n\t'
5
+ set -vx
6
+
7
+ bundle install
@@ -130,7 +130,7 @@ AllCops:
130
130
  # followed by the Gemfile.lock or gems.locked file. (Although the Ruby version
131
131
  # is specified in the Gemfile or gems.rb file, RuboCop reads the final value
132
132
  # from the lock file.) If the Ruby version is still unresolved, RuboCop will
133
- # use the oldest officially supported Ruby version (currently Ruby 2.1).
133
+ # use the oldest officially supported Ruby version (currently Ruby 2.2).
134
134
  TargetRubyVersion: ~
135
135
  # What version of Rails is the inspected code using? If a value is specified
136
136
  # for TargetRailsVersion then it is used. Acceptable values are specificed
@@ -739,6 +739,13 @@ Naming/HeredocDelimiterCase:
739
739
  - lowercase
740
740
  - uppercase
741
741
 
742
+ Naming/MemoizedInstanceVariableName:
743
+ EnforcedStyleForLeadingUnderscores: disallowed
744
+ SupportedStylesForLeadingUnderscores:
745
+ - disallowed
746
+ - required
747
+ - optional
748
+
742
749
  Naming/MethodName:
743
750
  EnforcedStyle: snake_case
744
751
  SupportedStyles:
@@ -1132,6 +1139,12 @@ Style/InverseMethods:
1132
1139
  :select: :reject
1133
1140
  :select!: :reject!
1134
1141
 
1142
+ Style/IpAddresses:
1143
+ # Allow strings to be whitelisted
1144
+ Whitelist:
1145
+ - "::"
1146
+ # :: is a valid IPv6 address, but could potentially be legitimately in code
1147
+
1135
1148
  Style/Lambda:
1136
1149
  EnforcedStyle: line_count_dependent
1137
1150
  SupportedStyles:
@@ -1539,7 +1552,10 @@ Metrics/AbcSize:
1539
1552
  Metrics/BlockLength:
1540
1553
  CountComments: false # count full line comments?
1541
1554
  Max: 25
1542
- ExcludedMethods: []
1555
+ ExcludedMethods:
1556
+ # By default, exclude the `#refine` method, as it tends to have larger
1557
+ # associated blocks.
1558
+ - refine
1543
1559
 
1544
1560
  Metrics/BlockNesting:
1545
1561
  CountBlocks: false
@@ -82,6 +82,10 @@ Style/InlineComment:
82
82
  Description: 'Avoid trailing inline comments.'
83
83
  Enabled: false
84
84
 
85
+ Style/IpAddresses:
86
+ Description: "Don't include literal IP addresses in code."
87
+ Enabled: false
88
+
85
89
  Style/MethodCallWithArgsParentheses:
86
90
  Description: 'Use parentheses for method calls with arguments.'
87
91
  StyleGuide: '#method-invocation-parens'
@@ -441,6 +441,7 @@ require_relative 'rubocop/cop/style/implicit_runtime_error'
441
441
  require_relative 'rubocop/cop/style/infinite_loop'
442
442
  require_relative 'rubocop/cop/style/inverse_methods'
443
443
  require_relative 'rubocop/cop/style/inline_comment'
444
+ require_relative 'rubocop/cop/style/ip_addresses'
444
445
  require_relative 'rubocop/cop/style/lambda'
445
446
  require_relative 'rubocop/cop/style/lambda_call'
446
447
  require_relative 'rubocop/cop/style/line_end_concatenation'
@@ -379,6 +379,11 @@ module RuboCop
379
379
  {equals_asgn? shorthand_asgn? asgn_method_call?}
380
380
  PATTERN
381
381
 
382
+ # Some cops treat the shovel operator as a kind of assignment.
383
+ def_node_matcher :assignment_or_similar?, <<-PATTERN
384
+ {assignment? (send _recv :<< ...)}
385
+ PATTERN
386
+
382
387
  def literal?
383
388
  LITERALS.include?(type)
384
389
  end
@@ -6,6 +6,8 @@ module RuboCop
6
6
  # in place of a plain node when the builder constructs the AST, making
7
7
  # its methods available to all `str` nodes within RuboCop.
8
8
  class StrNode < Node
9
+ include BasicLiteralNode
10
+
9
11
  def heredoc?
10
12
  loc.is_a?(Parser::Source::Map::Heredoc)
11
13
  end
@@ -39,7 +39,7 @@ module RuboCop
39
39
  act_on_options
40
40
  apply_default_formatter
41
41
  execute_runners(paths)
42
- rescue RuboCop::ConfigNotFoundError => e
42
+ rescue ConfigNotFoundError, IncorrectCopNameError, OptionArgumentError => e
43
43
  warn e.message
44
44
  STATUS_ERROR
45
45
  rescue RuboCop::Error => e
@@ -47,9 +47,6 @@ module RuboCop
47
47
  STATUS_ERROR
48
48
  rescue Finished
49
49
  STATUS_SUCCESS
50
- rescue IncorrectCopNameError => e
51
- warn e.message
52
- STATUS_ERROR
53
50
  rescue OptionParser::InvalidOption => e
54
51
  warn e.message
55
52
  warn 'For usage information, use --help'
@@ -128,9 +125,9 @@ module RuboCop
128
125
  def validate_options_vs_config
129
126
  if @options[:parallel] &&
130
127
  !@config_store.for(Dir.pwd).for_all_cops['UseCache']
131
- raise ArgumentError, '-P/--parallel uses caching to speed up ' \
132
- 'execution, so combining with AllCops: ' \
133
- 'UseCache: false is not allowed.'
128
+ raise OptionArgumentError, '-P/--parallel uses caching to speed up ' \
129
+ 'execution, so combining with AllCops: ' \
130
+ 'UseCache: false is not allowed.'
134
131
  end
135
132
  end
136
133
 
@@ -16,10 +16,10 @@ module RuboCop
16
16
 
17
17
  COMMON_PARAMS = %w[Exclude Include Severity
18
18
  AutoCorrect StyleGuide Details].freeze
19
- # 2.1 is the oldest officially supported Ruby version.
20
- DEFAULT_RUBY_VERSION = 2.1
21
- KNOWN_RUBIES = [2.1, 2.2, 2.3, 2.4, 2.5].freeze
22
- OBSOLETE_RUBIES = { 1.9 => '0.50', 2.0 => '0.50' }.freeze
19
+ # 2.2 is the oldest officially supported Ruby version.
20
+ DEFAULT_RUBY_VERSION = 2.2
21
+ KNOWN_RUBIES = [2.2, 2.3, 2.4, 2.5].freeze
22
+ OBSOLETE_RUBIES = { 1.9 => '0.50', 2.0 => '0.50', 2.1 => '0.58' }.freeze
23
23
  RUBY_VERSION_FILENAME = '.ruby-version'.freeze
24
24
  DEFAULT_RAILS_VERSION = 5.0
25
25
  OBSOLETE_COPS = {
@@ -176,15 +176,11 @@ module RuboCop
176
176
  end
177
177
 
178
178
  def yaml_safe_load(yaml_code, filename)
179
- if YAML.respond_to?(:safe_load) # Ruby 2.1+
180
- if defined?(SafeYAML) && SafeYAML.respond_to?(:load)
181
- SafeYAML.load(yaml_code, filename,
182
- whitelisted_tags: %w[!ruby/regexp])
183
- else
184
- YAML.safe_load(yaml_code, [Regexp, Symbol], [], false, filename)
185
- end
179
+ if defined?(SafeYAML) && SafeYAML.respond_to?(:load)
180
+ SafeYAML.load(yaml_code, filename,
181
+ whitelisted_tags: %w[!ruby/regexp])
186
182
  else
187
- YAML.load(yaml_code, filename) # rubocop:disable Security/YAMLLoad
183
+ YAML.safe_load(yaml_code, [Regexp, Symbol], [], false, filename)
188
184
  end
189
185
  end
190
186
  end
@@ -32,6 +32,8 @@ module RuboCop
32
32
  # corrector = Corrector.new(source_buffer, corrections)
33
33
  def initialize(source_buffer, corrections = [])
34
34
  @source_buffer = source_buffer
35
+ raise 'source_buffer should be a Parser::Source::Buffer' unless \
36
+ source_buffer.is_a? Parser::Source::Buffer
35
37
  @corrections = corrections
36
38
  @source_rewriter = Parser::Source::TreeRewriter.new(
37
39
  source_buffer,
@@ -72,6 +74,7 @@ module RuboCop
72
74
  #
73
75
  # @param [Parser::Source::Range] range
74
76
  def remove(range)
77
+ validate_range range
75
78
  @source_rewriter.remove(range)
76
79
  end
77
80
 
@@ -80,6 +83,7 @@ module RuboCop
80
83
  # @param [Parser::Source::Range] range
81
84
  # @param [String] content
82
85
  def insert_before(range, content)
86
+ validate_range range
83
87
  # TODO: Fix Cops using bad ranges instead
84
88
  if range.end_pos > @source_buffer.source.size
85
89
  range = range.with(end_pos: @source_buffer.source.size)
@@ -93,6 +97,7 @@ module RuboCop
93
97
  # @param [Parser::Source::Range] range
94
98
  # @param [String] content
95
99
  def insert_after(range, content)
100
+ validate_range range
96
101
  @source_rewriter.insert_after(range, content)
97
102
  end
98
103
 
@@ -101,6 +106,7 @@ module RuboCop
101
106
  # @param [Parser::Source::Range] range
102
107
  # @param [String] content
103
108
  def replace(range, content)
109
+ validate_range range
104
110
  @source_rewriter.replace(range, content)
105
111
  end
106
112
 
@@ -109,6 +115,7 @@ module RuboCop
109
115
  # @param [Parser::Source::Range] range
110
116
  # @param [Integer] size
111
117
  def remove_preceding(range, size)
118
+ validate_range range
112
119
  to_remove = Parser::Source::Range.new(range.source_buffer,
113
120
  range.begin_pos - size,
114
121
  range.begin_pos)
@@ -122,6 +129,7 @@ module RuboCop
122
129
  # @param [Parser::Source::Range] range
123
130
  # @param [Integer] size
124
131
  def remove_leading(range, size)
132
+ validate_range range
125
133
  to_remove = Parser::Source::Range.new(range.source_buffer,
126
134
  range.begin_pos,
127
135
  range.begin_pos + size)
@@ -135,11 +143,28 @@ module RuboCop
135
143
  # @param [Parser::Source::Range] range
136
144
  # @param [Integer] size
137
145
  def remove_trailing(range, size)
146
+ validate_range range
138
147
  to_remove = Parser::Source::Range.new(range.source_buffer,
139
148
  range.end_pos - size,
140
149
  range.end_pos)
141
150
  @source_rewriter.remove(to_remove)
142
151
  end
152
+
153
+ private
154
+
155
+ # :nodoc:
156
+ def validate_range(range)
157
+ return if range.source_buffer == @source_buffer
158
+ unless range.source_buffer.is_a?(Parser::Source::Buffer)
159
+ # actually this should be enforced by parser gem
160
+ raise 'Corrector expected range source buffer to be a '\
161
+ "Parser::Source::Buffer, but got #{range.source_buffer.class}"
162
+ end
163
+ raise "Correction target buffer #{range.source_buffer.object_id} "\
164
+ "name:#{range.source_buffer.name.inspect}"\
165
+ " is not current #{@source_buffer.object_id} "\
166
+ "name:#{@source_buffer.name.inspect} under investigation"
167
+ end
143
168
  end
144
169
  end
145
170
  end
@@ -55,13 +55,9 @@ module RuboCop
55
55
  'beginning of method definition.'.freeze
56
56
 
57
57
  def on_heredoc(node)
58
- return if heredoc_type(node) == SIMPLE_HEREDOC
59
-
60
- if empty_heredoc?(node) ||
61
- contents_indentation(node) >= closing_indentation(node)
62
- return if opening_indentation(node) == closing_indentation(node)
63
- return if argument_indentation_correct?(node)
64
- end
58
+ return if heredoc_type(node) == SIMPLE_HEREDOC ||
59
+ opening_indentation(node) == closing_indentation(node) ||
60
+ argument_indentation_correct?(node)
65
61
 
66
62
  add_offense(node, location: :heredoc_end)
67
63
  end
@@ -162,7 +162,7 @@ module RuboCop
162
162
  def alignment_node_for_variable_style(node)
163
163
  return node.parent if node.case_type? && node.argument?
164
164
 
165
- assignment = node.ancestors.find(&:assignment?)
165
+ assignment = node.ancestors.find(&:assignment_or_similar?)
166
166
  if assignment && !line_break_before_keyword?(assignment.source_range,
167
167
  node)
168
168
  assignment
@@ -103,7 +103,11 @@ module RuboCop
103
103
 
104
104
  def_end_config = config.for_cop('Layout/DefEndAlignment')
105
105
  style = def_end_config['EnforcedStyleAlignWith'] || 'start_of_line'
106
- base = style == 'def' ? node.first_argument : node
106
+ base = if style == 'def'
107
+ node.first_argument
108
+ else
109
+ leftmost_modifier_of(node) || node
110
+ end
107
111
 
108
112
  check_indentation(base.source_range, body)
109
113
  ignore_node(node.first_argument)
@@ -319,6 +323,10 @@ module RuboCop
319
323
  def configured_indentation_width
320
324
  cop_config['Width']
321
325
  end
326
+
327
+ def leftmost_modifier_of(node)
328
+ node.each_ancestor(:send).to_a.last
329
+ end
322
330
  end
323
331
  end
324
332
  end
@@ -39,7 +39,7 @@ module RuboCop
39
39
  end
40
40
 
41
41
  def autocorrect(node)
42
- range = Parser::Source::Range.new(processed_source.raw_source,
42
+ range = Parser::Source::Range.new(processed_source.buffer,
43
43
  0,
44
44
  node.begin_pos)
45
45
 
@@ -53,7 +53,7 @@ module RuboCop
53
53
  ALTERNATIVE_PROTECTED = '`protected` inside a `class << self` ' \
54
54
  'block'.freeze
55
55
 
56
- def_node_matcher :private_class_method, <<-PATTERN
56
+ def_node_search :private_class_methods, <<-PATTERN
57
57
  (send nil? :private_class_method $...)
58
58
  PATTERN
59
59
 
@@ -67,25 +67,26 @@ module RuboCop
67
67
 
68
68
  private
69
69
 
70
- def clear
71
- @useless = {}
72
- @last_access_modifier = nil
73
- end
74
-
75
70
  def check_node(node)
76
71
  return unless node && node.begin_type?
77
72
 
78
- clear
79
- check_scope(node)
73
+ ignored_methods = private_class_method_names(node)
80
74
 
81
- @useless.each do |_name, (defs_node, visibility, modifier)|
75
+ ineffective_modifier(node, ignored_methods) do |defs_node, modifier|
82
76
  add_offense(defs_node,
83
77
  location: :keyword,
84
- message: format_message(visibility, modifier))
78
+ message: format_message(modifier))
85
79
  end
86
80
  end
87
81
 
88
- def format_message(visibility, modifier)
82
+ def private_class_method_names(node)
83
+ private_class_methods(node).to_a.flatten
84
+ .select(&:basic_literal?)
85
+ .map(&:value)
86
+ end
87
+
88
+ def format_message(modifier)
89
+ visibility = modifier.method_name
89
90
  alternative = if visibility == :private
90
91
  ALTERNATIVE_PRIVATE
91
92
  else
@@ -96,52 +97,28 @@ module RuboCop
96
97
  alternative: alternative)
97
98
  end
98
99
 
99
- def check_scope(node, cur_vis = :public)
100
- node.each_child_node.reduce(cur_vis) do |visibility, child|
101
- check_child_scope(child, visibility)
102
- end
103
- end
104
-
105
- def check_child_scope(node, cur_vis)
106
- case node.type
107
- when :send
108
- cur_vis = check_send(node, cur_vis)
109
- when :defs
110
- check_defs(node, cur_vis)
111
- when :kwbegin
112
- check_scope(node, cur_vis)
100
+ def ineffective_modifier(node, ignored_methods, modifier = nil, &block)
101
+ node.each_child_node do |child|
102
+ case child.type
103
+ when :send
104
+ modifier = child if access_modifier?(child)
105
+ when :defs
106
+ next if correct_visibility?(child, modifier, ignored_methods)
107
+ yield child, modifier
108
+ when :kwbegin
109
+ ineffective_modifier(child, ignored_methods, modifier, &block)
110
+ end
113
111
  end
114
-
115
- cur_vis
116
112
  end
117
113
 
118
- def check_send(node, cur_vis)
119
- if node.bare_access_modifier? && !node.method?(:module_function)
120
- @last_access_modifier = node
121
- return node.method_name
122
- elsif (methods = private_class_method(node))
123
- # don't warn about defs nodes which are followed by a call to
124
- # `private_class_method :name`
125
- # obviously the programmer knows what they are doing
126
- revert_method_uselessness(methods)
127
- end
128
-
129
- cur_vis
114
+ def access_modifier?(node)
115
+ node.bare_access_modifier? && !node.method?(:module_function)
130
116
  end
131
117
 
132
- def check_defs(node, cur_vis)
133
- mark_method_as_useless(node, cur_vis) if cur_vis != :public
134
- end
118
+ def correct_visibility?(node, modifier, ignored_methods)
119
+ return true if modifier.nil? || modifier.method_name == :public
135
120
 
136
- def mark_method_as_useless(node, cur_vis)
137
- @useless[node.method_name] = [node, cur_vis, @last_access_modifier]
138
- end
139
-
140
- def revert_method_uselessness(methods)
141
- methods.each do |sym|
142
- next unless sym.sym_type?
143
- @useless.delete(sym.value)
144
- end
121
+ ignored_methods.include?(node.method_name)
145
122
  end
146
123
  end
147
124
  end