rubocop 0.85.0 → 0.85.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: edfaa1a180e5c318e672651ae7e4c75625a41a4c247d3ce378f6e25e809a4277
4
- data.tar.gz: 8ce15a32ddd8181b7b5117214d00a795f8824cbd13282120545fc7dd58548137
3
+ metadata.gz: 86b240fb0a653934db99c6ca12a1c53976c01f3d6de666842e9f7146f207f725
4
+ data.tar.gz: 606ea84dc8c148971ba8a2dc17ef2a03bbb48802e6053f5042d180e0f6c7c15f
5
5
  SHA512:
6
- metadata.gz: 57f80040236ae265d1478fecbbe20ebfd27905d52cd2c06d83202d2a8d7fe426feb774d63788fd38942872a12c18c247896e89f21c7010259ab1902cb5034177
7
- data.tar.gz: b86dcfcaa1839b9f3a7d532d87c1574e712df8018bfb3f85f06e4d2ecc1bb174018458fd2ee5f853def55f2312da8ab8b3037ea3be0defff4c16f4bbbeb6cf5d
6
+ metadata.gz: 4e6a817ec287b216c15e8820e29192fe1152fa77bb9b6fb9377f7a9b1bc22faef07e15c1f025691107914ae211f401549ef831c755fbf6b9c21bd04198388742
7
+ data.tar.gz: 2e3a6b4d35e5b24a9542ac5749247537ae2a47db242e86a189d3f719f7b1d35568eb22deaf491b2f5d1408de8415b2ee86fadfe8a56f229fbce89686744cc5e4
data/README.md CHANGED
@@ -1,3 +1,8 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/rubocop-hq/rubocop/master/logo/rubo-logo-horizontal.png" alt="RuboCop Logo"/>
3
+ </p>
4
+
5
+ ----------
1
6
  [![Gem Version](https://badge.fury.io/rb/rubocop.svg)](https://badge.fury.io/rb/rubocop)
2
7
  [![CircleCI Status](https://circleci.com/gh/rubocop-hq/rubocop/tree/master.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop/tree/master)
3
8
  [![Actions Status](https://github.com/rubocop-hq/rubocop/workflows/CI/badge.svg?branch=master)](https://github.com/rubocop-hq/rubocop/actions?query=workflow%3ACI)
@@ -11,26 +16,17 @@
11
16
  [![OpenCollective](https://opencollective.com/rubocop/sponsors/badge.svg)](#open-collective-sponsors)
12
17
  [![Tidelift](https://tidelift.com/badges/package/rubygems/rubocop)](https://tidelift.com/subscription/pkg/rubygems-rubocop?utm_source=rubygems-rubocop&utm_medium=referral&utm_campaign=readme)
13
18
 
14
- <p align="center">
15
- <img src="https://raw.githubusercontent.com/rubocop-hq/rubocop/master/logo/rubo-logo-horizontal.png" alt="RuboCop Logo"/>
16
- </p>
17
-
18
19
  > Role models are important. <br/>
19
20
  > -- Officer Alex J. Murphy / RoboCop
20
21
 
21
- **RuboCop** is a Ruby static code analyzer and code formatter. Out of
22
- the box it will enforce many of the guidelines outlined in the
23
- community [Ruby Style
24
- Guide](https://rubystyle.guide).
22
+ **RuboCop** is a Ruby static code analyzer (a.k.a. `linter`) and code formatter. Out of the box it
23
+ will enforce many of the guidelines outlined in the community [Ruby Style
24
+ Guide](https://rubystyle.guide). Apart from reporting the problems discovered in your code,
25
+ RuboCop can also automatically fix many of them you.
25
26
 
26
27
  RuboCop is extremely flexible and most aspects of its behavior can be tweaked via various
27
28
  [configuration options](https://github.com/rubocop-hq/rubocop/blob/master/config/default.yml).
28
29
 
29
- Apart from reporting problems in your code, RuboCop can also
30
- automatically fix some of the problems for you.
31
-
32
- [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bbatsov/rubocop?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
33
-
34
30
  **Please consider [financially supporting its ongoing development](#funding).**
35
31
 
36
32
  ## Installation
@@ -53,7 +49,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
53
49
  might want to use a conservative version lock in your `Gemfile`:
54
50
 
55
51
  ```rb
56
- gem 'rubocop', '~> 0.85.0', require: false
52
+ gem 'rubocop', '~> 0.85.1', require: false
57
53
  ```
58
54
 
59
55
  ## Quickstart
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # # Do custom processing
24
24
  # end
25
25
  # end
26
- class Cop
26
+ class Cop # rubocop:disable Metrics/ClassLength
27
27
  extend RuboCop::AST::Sexp
28
28
  extend NodePattern::Macros
29
29
  include RuboCop::AST::Sexp
@@ -44,22 +44,12 @@ module RuboCop
44
44
  attr_reader :config, :offenses, :corrections
45
45
  attr_accessor :processed_source # TODO: Bad design.
46
46
 
47
- @registry = Registry.new
48
-
49
- class << self
50
- attr_reader :registry
51
- end
52
-
53
- def self.all
54
- registry.without_department(:Test).cops
55
- end
56
-
57
- def self.qualified_cop_name(name, origin)
58
- registry.qualified_cop_name(name, origin)
47
+ def self.inherited(subclass)
48
+ Registry.global.enlist(subclass)
59
49
  end
60
50
 
61
- def self.inherited(subclass)
62
- registry.enlist(subclass)
51
+ def self.exclude_from_registry
52
+ Registry.global.dismiss(self)
63
53
  end
64
54
 
65
55
  def self.badge
@@ -246,6 +236,23 @@ module RuboCop
246
236
  nil
247
237
  end
248
238
 
239
+ ### Deprecated registry access
240
+
241
+ # @deprecated Use Registry.global
242
+ def self.registry
243
+ Registry.global
244
+ end
245
+
246
+ # @deprecated Use Registry.all
247
+ def self.all
248
+ Registry.all
249
+ end
250
+
251
+ # @deprecated Use Registry.qualified_cop_name
252
+ def self.qualified_cop_name(name, origin)
253
+ Registry.qualified_cop_name(name, origin)
254
+ end
255
+
249
256
  private
250
257
 
251
258
  def find_message(node, message)
@@ -147,7 +147,7 @@ module RuboCop
147
147
  def indent_level(str)
148
148
  indentations = str.lines
149
149
  .map { |line| line[/^\s*/] }
150
- .reject { |line| line == "\n" }
150
+ .reject { |line| line.end_with?("\n") }
151
151
  indentations.empty? ? 0 : indentations.min_by(&:size).size
152
152
  end
153
153
 
@@ -7,6 +7,10 @@ module RuboCop
7
7
  # expected fields for format/sprintf/#% and what is actually
8
8
  # passed as arguments.
9
9
  #
10
+ # In addition it checks whether different formats are used in the same
11
+ # format string. Do not mix numbered, unnumbered, and named formats in
12
+ # the same format string.
13
+ #
10
14
  # @example
11
15
  #
12
16
  # # bad
@@ -18,16 +22,37 @@ module RuboCop
18
22
  # # good
19
23
  #
20
24
  # format('A value: %s and another: %i', a_value, another)
25
+ #
26
+ # @example
27
+ #
28
+ # # bad
29
+ #
30
+ # format('Unnumbered format: %s and numbered: %2$s', a_value, another)
31
+ #
32
+ # @example
33
+ #
34
+ # # good
35
+ #
36
+ # format('Numbered format: %1$s and numbered %2$s', a_value, another)
21
37
  class FormatParameterMismatch < Cop
22
38
  # http://rubular.com/r/CvpbxkcTzy
23
39
  MSG = "Number of arguments (%<arg_num>i) to `%<method>s` doesn't " \
24
40
  'match the number of fields (%<field_num>i).'
41
+ MSG_INVALID = 'Format string is invalid because formatting sequence types ' \
42
+ '(numbered, named or unnumbered) are mixed.'
25
43
 
26
44
  KERNEL = 'Kernel'
27
45
  SHOVEL = '<<'
28
46
  STRING_TYPES = %i[str dstr].freeze
29
47
 
30
48
  def on_send(node)
49
+ return unless format_string?(node)
50
+
51
+ if invalid_format_string?(node)
52
+ add_offense(node, location: :selector, message: MSG_INVALID)
53
+ return
54
+ end
55
+
31
56
  return unless offending_node?(node)
32
57
 
33
58
  add_offense(node, location: :selector)
@@ -35,9 +60,15 @@ module RuboCop
35
60
 
36
61
  private
37
62
 
63
+ def format_string?(node)
64
+ called_on_string?(node) && method_with_format_args?(node)
65
+ end
66
+
67
+ def invalid_format_string?(node)
68
+ !RuboCop::Cop::Utils::FormatString.new(node.source).valid?
69
+ end
70
+
38
71
  def offending_node?(node)
39
- return false unless called_on_string?(node)
40
- return false unless method_with_format_args?(node)
41
72
  return false if splat_args?(node)
42
73
 
43
74
  num_of_format_args, num_of_expected_fields = count_matches(node)
@@ -27,7 +27,14 @@ module RuboCop
27
27
  def on_regexp(node)
28
28
  return if contain_non_literal?(node)
29
29
 
30
- tree = Regexp::Parser.parse(node.content)
30
+ begin
31
+ tree = Regexp::Parser.parse(node.content)
32
+ # Returns if a regular expression that cannot be processed by regexp_parser gem.
33
+ # https://github.com/rubocop-hq/rubocop/issues/8083
34
+ rescue Regexp::Scanner::ScannerError
35
+ return
36
+ end
37
+
31
38
  return unless named_capture?(tree)
32
39
  return unless numbered_capture?(tree)
33
40
 
@@ -78,7 +78,7 @@ module RuboCop
78
78
 
79
79
  def comment_between_rescue_and_end?(node)
80
80
  end_line = nil
81
- node.each_ancestor(:kwbegin, :def) do |ancestor|
81
+ node.each_ancestor(:kwbegin, :def, :defs, :block) do |ancestor|
82
82
  end_line = ancestor.loc.end.line
83
83
  break
84
84
  end
@@ -22,34 +22,42 @@ module RuboCop
22
22
 
23
23
  # Registry that tracks all cops by their badge and department.
24
24
  class Registry
25
+ include Enumerable
26
+
27
+ attr_reader :options
28
+
25
29
  def initialize(cops = [], options = {})
26
30
  @registry = {}
27
31
  @departments = {}
28
32
  @cops_by_cop_name = Hash.new { |hash, key| hash[key] = [] }
29
33
 
30
- cops.each { |cop| enlist(cop) }
34
+ @enrollment_queue = cops
31
35
  @options = options
32
36
  end
33
37
 
34
38
  def enlist(cop)
35
- @registry[cop.badge] = cop
36
- @departments[cop.department] ||= []
37
- @departments[cop.department] << cop
38
- @cops_by_cop_name[cop.cop_name] << cop
39
+ @enrollment_queue << cop
40
+ end
41
+
42
+ def dismiss(cop)
43
+ raise "Cop #{cop} could not be dismissed" unless @enrollment_queue.delete(cop)
39
44
  end
40
45
 
41
46
  # @return [Array<Symbol>] list of departments for current cops.
42
47
  def departments
48
+ clear_enrollment_queue
43
49
  @departments.keys
44
50
  end
45
51
 
46
52
  # @return [Registry] Cops for that specific department.
47
53
  def with_department(department)
54
+ clear_enrollment_queue
48
55
  with(@departments.fetch(department, []))
49
56
  end
50
57
 
51
58
  # @return [Registry] Cops not for a specific department.
52
59
  def without_department(department)
60
+ clear_enrollment_queue
53
61
  without_department = @departments.dup
54
62
  without_department.delete(department)
55
63
 
@@ -119,6 +127,7 @@ module RuboCop
119
127
  end
120
128
 
121
129
  def unqualified_cop_names
130
+ clear_enrollment_queue
122
131
  @unqualified_cop_names ||=
123
132
  Set.new(@cops_by_cop_name.keys.map { |qn| File.basename(qn) }) <<
124
133
  'RedundantCopDisableDirective'
@@ -126,18 +135,21 @@ module RuboCop
126
135
 
127
136
  # @return [Hash{String => Array<Class>}]
128
137
  def to_h
138
+ clear_enrollment_queue
129
139
  @cops_by_cop_name
130
140
  end
131
141
 
132
142
  def cops
143
+ clear_enrollment_queue
133
144
  @registry.values
134
145
  end
135
146
 
136
147
  def length
148
+ clear_enrollment_queue
137
149
  @registry.size
138
150
  end
139
151
 
140
- def enabled(config, only, only_safe = false)
152
+ def enabled(config, only = [], only_safe = false)
141
153
  select do |cop|
142
154
  only.include?(cop.cop_name) || enabled?(cop, config, only_safe)
143
155
  end
@@ -172,6 +184,7 @@ module RuboCop
172
184
  end
173
185
 
174
186
  def sort!
187
+ clear_enrollment_queue
175
188
  @registry = Hash[@registry.sort_by { |badge, _| badge.cop_name }]
176
189
 
177
190
  self
@@ -188,16 +201,57 @@ module RuboCop
188
201
  # @param [String] cop_name
189
202
  # @return [Class, nil]
190
203
  def find_by_cop_name(cop_name)
191
- @cops_by_cop_name[cop_name].first
204
+ to_h[cop_name].first
205
+ end
206
+
207
+ @global = new
208
+
209
+ class << self
210
+ attr_reader :global
211
+ end
212
+
213
+ def self.all
214
+ global.without_department(:Test).cops
215
+ end
216
+
217
+ def self.qualified_cop_name(name, origin)
218
+ global.qualified_cop_name(name, origin)
219
+ end
220
+
221
+ # Changes momentarily the global registry
222
+ # Intended for testing purposes
223
+ def self.with_temporary_global(temp_global = global.dup)
224
+ previous = @global
225
+ @global = temp_global
226
+ yield
227
+ ensure
228
+ @global = previous
192
229
  end
193
230
 
194
231
  private
195
232
 
233
+ def initialize_copy(reg)
234
+ initialize(reg.cops, reg.options)
235
+ end
236
+
237
+ def clear_enrollment_queue
238
+ return if @enrollment_queue.empty?
239
+
240
+ @enrollment_queue.each do |cop|
241
+ @registry[cop.badge] = cop
242
+ @departments[cop.department] ||= []
243
+ @departments[cop.department] << cop
244
+ @cops_by_cop_name[cop.cop_name] << cop
245
+ end
246
+ @enrollment_queue = []
247
+ end
248
+
196
249
  def with(cops)
197
250
  self.class.new(cops)
198
251
  end
199
252
 
200
253
  def qualify_badge(badge)
254
+ clear_enrollment_queue
201
255
  @departments
202
256
  .map { |department, _| badge.with_department(department) }
203
257
  .select { |potential_badge| registered?(potential_badge) }
@@ -214,6 +268,7 @@ module RuboCop
214
268
  end
215
269
 
216
270
  def registered?(badge)
271
+ clear_enrollment_queue
217
272
  @registry.key?(badge)
218
273
  end
219
274
  end
@@ -27,7 +27,8 @@ module RuboCop
27
27
  class RedundantConditional < Cop
28
28
  include Alignment
29
29
 
30
- COMPARISON_OPERATORS = RuboCop::AST::Node::COMPARISON_OPERATORS
30
+ operators = RuboCop::AST::Node::COMPARISON_OPERATORS.to_a
31
+ COMPARISON_OPERATOR_MATCHER = "{:#{operators.join(' :')}}"
31
32
 
32
33
  MSG = 'This conditional expression can just be replaced ' \
33
34
  'by `%<msg>s`.'
@@ -54,11 +55,11 @@ module RuboCop
54
55
  end
55
56
 
56
57
  def_node_matcher :redundant_condition?, <<~RUBY
57
- (if (send _ {:#{COMPARISON_OPERATORS.join(' :')}} _) true false)
58
+ (if (send _ #{COMPARISON_OPERATOR_MATCHER} _) true false)
58
59
  RUBY
59
60
 
60
61
  def_node_matcher :redundant_condition_inverted?, <<~RUBY
61
- (if (send _ {:#{COMPARISON_OPERATORS.join(' :')}} _) false true)
62
+ (if (send _ #{COMPARISON_OPERATOR_MATCHER} _) false true)
62
63
  RUBY
63
64
 
64
65
  def offense?(node)
@@ -31,6 +31,14 @@ module RuboCop
31
31
  validate_config
32
32
  end
33
33
 
34
+ # @return [Team]
35
+ def self.new(cop_or_classes, config, options = {})
36
+ # Support v0 api:
37
+ return mobilize(cop_or_classes, config, options) if cop_or_classes.first.is_a?(Class)
38
+
39
+ super
40
+ end
41
+
34
42
  # @return [Team] with cops assembled from the given `cop_classes`
35
43
  def self.mobilize(cop_classes, config, options = nil)
36
44
  options ||= DEFAULT_OPTIONS
@@ -97,6 +97,10 @@ module RuboCop
97
97
  @format_sequences ||= parse
98
98
  end
99
99
 
100
+ def valid?
101
+ !mixed_formats?
102
+ end
103
+
100
104
  def named_interpolation?
101
105
  format_sequences.any?(&:name)
102
106
  end
@@ -114,6 +118,20 @@ module RuboCop
114
118
  )
115
119
  end
116
120
  end
121
+
122
+ def mixed_formats?
123
+ formats = format_sequences.map do |seq|
124
+ if seq.name
125
+ :named
126
+ elsif seq.max_digit_dollar_num
127
+ :numbered
128
+ else
129
+ :unnumbered
130
+ end
131
+ end
132
+
133
+ formats.uniq.size > 1
134
+ end
117
135
  end
118
136
  end
119
137
  end
@@ -115,7 +115,7 @@ module RuboCop
115
115
  expect(actual_annotations.to_s).to eq(expected_annotations.to_s)
116
116
  end
117
117
 
118
- def expect_correction(correction, loop: false)
118
+ def expect_correction(correction, loop: true)
119
119
  raise '`expect_correction` must follow `expect_offense`' unless @processed_source
120
120
 
121
121
  iteration = 0
@@ -128,6 +128,7 @@ module RuboCop
128
128
 
129
129
  break corrected_source unless loop
130
130
  break corrected_source if cop.corrections.empty?
131
+ break corrected_source if corrected_source == @processed_source.buffer.source
131
132
 
132
133
  if iteration > RuboCop::Runner::MAX_ITERATIONS
133
134
  raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [])
@@ -135,6 +136,8 @@ module RuboCop
135
136
 
136
137
  # Prepare for next loop
137
138
  cop.instance_variable_set(:@corrections, [])
139
+ # Cache invalidation. This is bad!
140
+ cop.instance_variable_set(:@token_table, nil)
138
141
  @processed_source = parse_source(corrected_source,
139
142
  @processed_source.path)
140
143
  _investigate(cop, @processed_source)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '0.85.0'
6
+ STRING = '0.85.1'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, '\
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.85.0
4
+ version: 0.85.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2020-06-01 00:00:00.000000000 Z
13
+ date: 2020-06-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parallel
@@ -157,8 +157,8 @@ dependencies:
157
157
  - !ruby/object:Gem::Version
158
158
  version: '3.0'
159
159
  description: |2
160
- Automatic Ruby code style checking tool.
161
- Aims to enforce the community-driven Ruby Style Guide.
160
+ RuboCop is a Ruby code style checking and code formatting tool.
161
+ It aims to enforce the community-driven Ruby Style Guide.
162
162
  email: rubocop@googlegroups.com
163
163
  executables:
164
164
  - rubocop