rubocop 1.62.1 → 1.63.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +23 -3
- data/lib/rubocop/config.rb +33 -10
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/cop/base.rb +37 -0
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -2
- data/lib/rubocop/cop/lint/debugger.rb +27 -2
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
- data/lib/rubocop/cop/mixin/code_length.rb +12 -1
- data/lib/rubocop/cop/mixin/method_complexity.rb +15 -6
- data/lib/rubocop/cop/mixin/safe_assignment.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +31 -12
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/inclusive_language.rb +1 -2
- data/lib/rubocop/cop/style/alias.rb +1 -0
- data/lib/rubocop/cop/style/collection_compact.rb +3 -3
- data/lib/rubocop/cop/style/copyright.rb +16 -11
- data/lib/rubocop/cop/style/eval_with_location.rb +3 -1
- data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
- data/lib/rubocop/cop/style/format_string.rb +9 -9
- data/lib/rubocop/cop/style/map_into_array.rb +175 -0
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
- data/lib/rubocop/cop/style/map_to_set.rb +1 -1
- data/lib/rubocop/cop/style/redundant_argument.rb +24 -1
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
- data/lib/rubocop/cop/style/redundant_each.rb +1 -1
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +8 -14
- data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/team.rb +3 -0
- data/lib/rubocop/formatter/clang_style_formatter.rb +3 -7
- data/lib/rubocop/formatter/tap_formatter.rb +3 -7
- data/lib/rubocop/lockfile.rb +46 -7
- data/lib/rubocop/rspec/expect_offense.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +13 -1
- data/lib/rubocop/runner.rb +3 -0
- data/lib/rubocop/version.rb +2 -2
- data/lib/rubocop.rb +1 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e73eb2f54012899b388f6a92a079c80233fe1dfc570abfeedf2091cb0abc71d
|
4
|
+
data.tar.gz: cc1ef25acb1541f1873ebeb945254c430b175b468bc9645000008a5f7d9aea88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4a4d23a528df832913cd6e469942415f2e9af4b2c8893862f7cd2dada8da960f8fbd3525934ab325d290a0161a576c38318222edc55e26a150596ca99235d11
|
7
|
+
data.tar.gz: c84f81ba55dd33a44a42ea9851d910738e8faffc3a02b0e6095eb17b17e4e1bf5fa90a0db5aac365a58bde4ff748782f6d6941575fb506122bc4afd2627b966c
|
data/README.md
CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
|
|
53
53
|
in your `Gemfile`:
|
54
54
|
|
55
55
|
```rb
|
56
|
-
gem 'rubocop', '~> 1.
|
56
|
+
gem 'rubocop', '~> 1.63', require: false
|
57
57
|
```
|
58
58
|
|
59
59
|
See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
|
data/config/default.yml
CHANGED
@@ -156,13 +156,14 @@ AllCops:
|
|
156
156
|
# included.
|
157
157
|
SuggestExtensions:
|
158
158
|
rubocop-rails: [rails]
|
159
|
-
rubocop-rspec: [rspec
|
159
|
+
rubocop-rspec: [rspec]
|
160
160
|
rubocop-minitest: [minitest]
|
161
161
|
rubocop-sequel: [sequel]
|
162
162
|
rubocop-rake: [rake]
|
163
163
|
rubocop-graphql: [graphql]
|
164
164
|
rubocop-capybara: [capybara]
|
165
165
|
rubocop-factory_bot: [factory_bot, factory_bot_rails]
|
166
|
+
rubocop-rspec_rails: [rspec-rails]
|
166
167
|
# Enable/Disable checking the methods extended by Active Support.
|
167
168
|
ActiveSupportExtensionsEnabled: false
|
168
169
|
|
@@ -1657,7 +1658,7 @@ Lint/Debugger:
|
|
1657
1658
|
Description: 'Check for debugger calls.'
|
1658
1659
|
Enabled: true
|
1659
1660
|
VersionAdded: '0.14'
|
1660
|
-
VersionChanged: '1.
|
1661
|
+
VersionChanged: '1.63'
|
1661
1662
|
DebuggerMethods:
|
1662
1663
|
# Groups are available so that a specific group can be disabled in
|
1663
1664
|
# a user's configuration, but are otherwise not significant.
|
@@ -1670,8 +1671,14 @@ Lint/Debugger:
|
|
1670
1671
|
- Kernel.byebug
|
1671
1672
|
- Kernel.remote_byebug
|
1672
1673
|
Capybara:
|
1674
|
+
- page.save_and_open_page
|
1675
|
+
- page.save_and_open_screenshot
|
1676
|
+
- page.save_page
|
1677
|
+
- page.save_screenshot
|
1673
1678
|
- save_and_open_page
|
1674
1679
|
- save_and_open_screenshot
|
1680
|
+
- save_page
|
1681
|
+
- save_screenshot
|
1675
1682
|
debug.rb:
|
1676
1683
|
- binding.b
|
1677
1684
|
- binding.break
|
@@ -1693,6 +1700,11 @@ Lint/Debugger:
|
|
1693
1700
|
- jard
|
1694
1701
|
WebConsole:
|
1695
1702
|
- binding.console
|
1703
|
+
DebuggerRequires:
|
1704
|
+
debug.rb:
|
1705
|
+
- debug/open
|
1706
|
+
- debug/open_nonstop
|
1707
|
+
- debug/start
|
1696
1708
|
|
1697
1709
|
Lint/DeprecatedClassMethods:
|
1698
1710
|
Description: 'Check for deprecated class method calls.'
|
@@ -2762,7 +2774,8 @@ Naming/FileName:
|
|
2762
2774
|
VersionChanged: '1.23'
|
2763
2775
|
# Camel case file names listed in `AllCops:Include` and all file names listed
|
2764
2776
|
# in `AllCops:Exclude` are excluded by default. Add extra excludes here.
|
2765
|
-
Exclude:
|
2777
|
+
Exclude:
|
2778
|
+
- Rakefile.rb
|
2766
2779
|
# When `true`, requires that each source file should define a class or module
|
2767
2780
|
# with a name which matches the file name (converted to ... case).
|
2768
2781
|
# It further expects it to be nested inside modules which match the names
|
@@ -4276,6 +4289,13 @@ Style/MapCompactWithConditionalBlock:
|
|
4276
4289
|
Enabled: pending
|
4277
4290
|
VersionAdded: '1.30'
|
4278
4291
|
|
4292
|
+
Style/MapIntoArray:
|
4293
|
+
Description: 'Checks for usages of `each` with `<<`, `push`, or `append` which can be replaced by `map`.'
|
4294
|
+
StyleGuide: '#functional-code'
|
4295
|
+
Enabled: pending
|
4296
|
+
VersionAdded: '1.63'
|
4297
|
+
Safe: false
|
4298
|
+
|
4279
4299
|
Style/MapToHash:
|
4280
4300
|
Description: 'Prefer `to_h` with a block over `map.to_h`.'
|
4281
4301
|
Enabled: pending
|
data/lib/rubocop/config.rb
CHANGED
@@ -263,6 +263,7 @@ module RuboCop
|
|
263
263
|
PathUtil.smart_path(@loaded_path)
|
264
264
|
end
|
265
265
|
|
266
|
+
# @return [String, nil]
|
266
267
|
def bundler_lock_file_path
|
267
268
|
return nil unless loaded_path
|
268
269
|
|
@@ -286,27 +287,49 @@ module RuboCop
|
|
286
287
|
end
|
287
288
|
end
|
288
289
|
|
290
|
+
# Returns target's locked gem versions (i.e. from Gemfile.lock or gems.locked)
|
291
|
+
# @returns [Hash{String => Gem::Version}] The locked gem versions, keyed by the gems' names.
|
292
|
+
def gem_versions_in_target
|
293
|
+
@gem_versions_in_target ||= read_gem_versions_from_target_lockfile
|
294
|
+
end
|
295
|
+
|
289
296
|
def inspect # :nodoc:
|
290
297
|
"#<#{self.class.name}:#{object_id} @loaded_path=#{loaded_path}>"
|
291
298
|
end
|
292
299
|
|
293
300
|
private
|
294
301
|
|
302
|
+
# @return [Float, nil] The Rails version as a `major.minor` Float.
|
295
303
|
def target_rails_version_from_bundler_lock_file
|
296
304
|
@target_rails_version_from_bundler_lock_file ||= read_rails_version_from_bundler_lock_file
|
297
305
|
end
|
298
306
|
|
307
|
+
# @return [Float, nil] The Rails version as a `major.minor` Float.
|
299
308
|
def read_rails_version_from_bundler_lock_file
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
309
|
+
return nil unless gem_versions_in_target
|
310
|
+
|
311
|
+
# Look for `railties` instead of `rails`, to support apps that only use a subset of `rails`
|
312
|
+
# See https://github.com/rubocop/rubocop/pull/11289
|
313
|
+
rails_version_in_target = gem_versions_in_target['railties']
|
314
|
+
return nil unless rails_version_in_target
|
315
|
+
|
316
|
+
gem_version_to_major_minor_float(rails_version_in_target)
|
317
|
+
end
|
318
|
+
|
319
|
+
# @param [Gem::Version] gem_version an object like `Gem::Version.new("7.1.2.3")`
|
320
|
+
# @return [Float] The major and minor version, like `7.1`
|
321
|
+
def gem_version_to_major_minor_float(gem_version)
|
322
|
+
segments = gem_version.canonical_segments
|
323
|
+
# segments.fetch(0).to_f + (segments.fetch(1, 0.0).to_f / 10)
|
324
|
+
Float("#{segments.fetch(0)}.#{segments.fetch(1, 0)}")
|
325
|
+
end
|
326
|
+
|
327
|
+
# @returns [Hash{String => Gem::Version}] The locked gem versions, keyed by the gems' names.
|
328
|
+
def read_gem_versions_from_target_lockfile
|
329
|
+
lockfile_path = bundler_lock_file_path
|
330
|
+
return nil unless lockfile_path
|
331
|
+
|
332
|
+
Lockfile.new(lockfile_path).gem_versions
|
310
333
|
end
|
311
334
|
|
312
335
|
def enable_cop?(qualified_cop_name, cop_options)
|
@@ -52,7 +52,7 @@ module RuboCop
|
|
52
52
|
def load_rules # rubocop:disable Metrics/AbcSize
|
53
53
|
rules = LOAD_RULES_CACHE[self.class.files] ||=
|
54
54
|
self.class.files.each_with_object({}) do |filename, hash|
|
55
|
-
hash.merge!(YAML.safe_load(File.read(filename))) do |_key, first, second|
|
55
|
+
hash.merge!(YAML.safe_load(File.read(filename)) || {}) do |_key, first, second|
|
56
56
|
case first
|
57
57
|
when Hash
|
58
58
|
first.merge(second)
|
data/lib/rubocop/cop/base.rb
CHANGED
@@ -70,6 +70,7 @@ module RuboCop
|
|
70
70
|
|
71
71
|
def self.inherited(subclass)
|
72
72
|
super
|
73
|
+
subclass.instance_variable_set(:@gem_requirements, gem_requirements.dup)
|
73
74
|
Registry.global.enlist(subclass)
|
74
75
|
end
|
75
76
|
|
@@ -126,6 +127,29 @@ module RuboCop
|
|
126
127
|
false
|
127
128
|
end
|
128
129
|
|
130
|
+
## Gem requirements
|
131
|
+
|
132
|
+
@gem_requirements = {}
|
133
|
+
|
134
|
+
class << self
|
135
|
+
attr_reader :gem_requirements
|
136
|
+
|
137
|
+
# Register a version requirement for the given gem name.
|
138
|
+
# This cop will be skipped unless the target satisfies *all* requirements.
|
139
|
+
# @param [String] gem_name
|
140
|
+
# @param [Array<String>] version_requirements The version requirements,
|
141
|
+
# using the same syntax as a Gemfile, e.g. ">= 1.2.3"
|
142
|
+
#
|
143
|
+
# If omitted, any version of the gem will be accepted.
|
144
|
+
#
|
145
|
+
# https://guides.rubygems.org/patterns/#declaring-dependencies
|
146
|
+
#
|
147
|
+
# @api public
|
148
|
+
def requires_gem(gem_name, *version_requirements)
|
149
|
+
@gem_requirements[gem_name] = Gem::Requirement.new(version_requirements)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
129
153
|
def initialize(config = nil, options = nil)
|
130
154
|
@config = config || Config.new
|
131
155
|
@options = options || { debug: false }
|
@@ -245,6 +269,7 @@ module RuboCop
|
|
245
269
|
end
|
246
270
|
|
247
271
|
def relevant_file?(file)
|
272
|
+
return false unless target_satisfies_all_gem_version_requirements?
|
248
273
|
return true unless @config.clusivity_config_for_badge?(self.class.badge)
|
249
274
|
|
250
275
|
file == RuboCop::AST::ProcessedSource::STRING_SOURCE_NAME ||
|
@@ -496,6 +521,18 @@ module RuboCop
|
|
496
521
|
range.end_pos + @current_offset
|
497
522
|
)
|
498
523
|
end
|
524
|
+
|
525
|
+
def target_satisfies_all_gem_version_requirements?
|
526
|
+
self.class.gem_requirements.all? do |gem_name, version_req|
|
527
|
+
all_gem_versions_in_target = @config.gem_versions_in_target
|
528
|
+
next false unless all_gem_versions_in_target
|
529
|
+
|
530
|
+
gem_version_in_target = all_gem_versions_in_target[gem_name]
|
531
|
+
next false unless gem_version_in_target
|
532
|
+
|
533
|
+
version_req.satisfied_by?(gem_version_in_target)
|
534
|
+
end
|
535
|
+
end
|
499
536
|
end
|
500
537
|
end
|
501
538
|
end
|
@@ -45,6 +45,7 @@ module RuboCop
|
|
45
45
|
EXPECT_OFFENSE_DESCRIPTION_MAPPING = {
|
46
46
|
/\A(does not|doesn't) (register|find|flag|report)/ => 'registers',
|
47
47
|
/\A(does not|doesn't) add (a|an|any )?offense/ => 'registers an offense',
|
48
|
+
/\Aregisters no offense/ => 'registers an offense',
|
48
49
|
/\Aaccepts\b/ => 'registers'
|
49
50
|
}.freeze
|
50
51
|
|
@@ -50,7 +50,7 @@ module RuboCop
|
|
50
50
|
MSG_WITHOUT_SAFE_ASSIGNMENT_ALLOWED =
|
51
51
|
'Use `==` if you meant to do a comparison or move the assignment ' \
|
52
52
|
'up out of the condition.'
|
53
|
-
ASGN_TYPES = [:begin, *AST::Node::EQUALS_ASSIGNMENTS, :send].freeze
|
53
|
+
ASGN_TYPES = [:begin, *AST::Node::EQUALS_ASSIGNMENTS, :send, :csend].freeze
|
54
54
|
|
55
55
|
def on_if(node)
|
56
56
|
return if node.condition.block_type?
|
@@ -88,7 +88,7 @@ module RuboCop
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def skip_children?(asgn_node)
|
91
|
-
(asgn_node.
|
91
|
+
(asgn_node.call_type? && !asgn_node.assignment_method?) ||
|
92
92
|
empty_condition?(asgn_node) ||
|
93
93
|
(safe_assignment_allowed? && safe_assignment?(asgn_node))
|
94
94
|
end
|
@@ -29,6 +29,11 @@ module RuboCop
|
|
29
29
|
# MyDebugger.debug_this
|
30
30
|
# ----
|
31
31
|
#
|
32
|
+
# Some gems also ship files that will start a debugging session when required,
|
33
|
+
# for example `require 'debug/start'` from `ruby/debug`. These requires can
|
34
|
+
# be configured through `DebuggerRequires`. It has the same structure as
|
35
|
+
# `DebuggerMethods`, which you can read about above.
|
36
|
+
#
|
32
37
|
# @example
|
33
38
|
#
|
34
39
|
# # bad (ok during development)
|
@@ -64,14 +69,20 @@ module RuboCop
|
|
64
69
|
# def some_method
|
65
70
|
# my_debugger
|
66
71
|
# end
|
72
|
+
#
|
73
|
+
# @example DebuggerRequires: [my_debugger/start]
|
74
|
+
#
|
75
|
+
# # bad (ok during development)
|
76
|
+
#
|
77
|
+
# require 'my_debugger/start'
|
67
78
|
class Debugger < Base
|
68
79
|
MSG = 'Remove debugger entry point `%<source>s`.'
|
69
80
|
BLOCK_TYPES = %i[block numblock kwbegin].freeze
|
70
81
|
|
71
82
|
def on_send(node)
|
72
|
-
return if
|
83
|
+
return if assumed_usage_context?(node)
|
73
84
|
|
74
|
-
add_offense(node)
|
85
|
+
add_offense(node) if debugger_method?(node) || debugger_require?(node)
|
75
86
|
end
|
76
87
|
|
77
88
|
private
|
@@ -87,12 +98,26 @@ module RuboCop
|
|
87
98
|
end
|
88
99
|
end
|
89
100
|
|
101
|
+
def debugger_requires
|
102
|
+
@debugger_requires ||= begin
|
103
|
+
config = cop_config.fetch('DebuggerRequires', [])
|
104
|
+
config.is_a?(Array) ? config : config.values.flatten
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
90
108
|
def debugger_method?(send_node)
|
91
109
|
return false if send_node.parent&.send_type? && send_node.parent.receiver == send_node
|
92
110
|
|
93
111
|
debugger_methods.include?(chained_method_name(send_node))
|
94
112
|
end
|
95
113
|
|
114
|
+
def debugger_require?(send_node)
|
115
|
+
return false unless send_node.method?(:require) && send_node.arguments.one?
|
116
|
+
return false unless (argument = send_node.first_argument).str_type?
|
117
|
+
|
118
|
+
debugger_requires.include?(argument.value)
|
119
|
+
end
|
120
|
+
|
96
121
|
def assumed_usage_context?(node)
|
97
122
|
# Basically, debugger methods are not used as a method argument without arguments.
|
98
123
|
return false unless node.arguments.empty? && node.each_ancestor(:send, :csend).any?
|
@@ -146,7 +146,7 @@ module RuboCop
|
|
146
146
|
node.source_range.with(end_pos: node.condition.source_range.end_pos)
|
147
147
|
elsif all_branches_body_missing?(node)
|
148
148
|
if_node = node.ancestors.detect(&:if?)
|
149
|
-
node.source_range.
|
149
|
+
node.source_range.join(if_node.loc.end.end)
|
150
150
|
else
|
151
151
|
node.source_range
|
152
152
|
end
|
@@ -130,7 +130,7 @@ module RuboCop
|
|
130
130
|
# @return [Parser::Source::Range]
|
131
131
|
#
|
132
132
|
def last_arg_range(node)
|
133
|
-
node.last_argument.source_range.
|
133
|
+
node.last_argument.source_range.join(node.arguments[-2].source_range.end)
|
134
134
|
end
|
135
135
|
|
136
136
|
def unsorted_dir_loop?(node)
|
@@ -33,8 +33,10 @@ module RuboCop
|
|
33
33
|
MSG_EACH_WITH_INDEX = 'Use `each` instead of `each_with_index`.'
|
34
34
|
MSG_WITH_INDEX = 'Remove redundant `with_index`.'
|
35
35
|
|
36
|
+
# rubocop:disable Metrics/AbcSize
|
36
37
|
def on_block(node)
|
37
38
|
return unless node.receiver
|
39
|
+
return if node.method?(:with_index) && !node.receiver.receiver
|
38
40
|
return unless (send = redundant_with_index?(node))
|
39
41
|
|
40
42
|
range = with_index_range(send)
|
@@ -48,6 +50,7 @@ module RuboCop
|
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
53
|
+
# rubocop:enable Metrics/AbcSize
|
51
54
|
|
52
55
|
alias on_numblock on_block
|
53
56
|
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
length = calculator.calculate
|
37
37
|
return if length <= max_length
|
38
38
|
|
39
|
-
location = node
|
39
|
+
location = location(node)
|
40
40
|
|
41
41
|
add_offense(location, message: message(length, max_length)) { self.max = length }
|
42
42
|
end
|
@@ -54,6 +54,17 @@ module RuboCop
|
|
54
54
|
foldable_types: count_as_one
|
55
55
|
)
|
56
56
|
end
|
57
|
+
|
58
|
+
def location(node)
|
59
|
+
return node.loc.name if node.casgn_type?
|
60
|
+
|
61
|
+
if LSP.enabled?
|
62
|
+
end_range = node.loc.respond_to?(:name) ? node.loc.name : node.loc.begin
|
63
|
+
node.source_range.begin.join(end_range)
|
64
|
+
else
|
65
|
+
node.source_range
|
66
|
+
end
|
67
|
+
end
|
57
68
|
end
|
58
69
|
end
|
59
70
|
end
|
@@ -49,13 +49,13 @@ module RuboCop
|
|
49
49
|
|
50
50
|
return unless complexity > max
|
51
51
|
|
52
|
-
msg = format(
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
msg = format(
|
53
|
+
self.class::MSG,
|
54
|
+
method: method_name, complexity: complexity, abc_vector: abc_vector, max: max
|
55
|
+
)
|
56
|
+
location = location(node)
|
57
57
|
|
58
|
-
add_offense(
|
58
|
+
add_offense(location, message: msg) { self.max = complexity.ceil }
|
59
59
|
end
|
60
60
|
|
61
61
|
def complexity(body)
|
@@ -69,6 +69,15 @@ module RuboCop
|
|
69
69
|
end
|
70
70
|
score
|
71
71
|
end
|
72
|
+
|
73
|
+
def location(node)
|
74
|
+
if LSP.enabled?
|
75
|
+
end_range = node.loc.respond_to?(:name) ? node.loc.name : node.loc.begin
|
76
|
+
node.source_range.begin.join(end_range)
|
77
|
+
else
|
78
|
+
node.source_range
|
79
|
+
end
|
80
|
+
end
|
72
81
|
end
|
73
82
|
end
|
74
83
|
end
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
14
14
|
def_node_matcher :empty_condition?, '(begin)'
|
15
15
|
|
16
16
|
# @!method setter_method?(node)
|
17
|
-
def_node_matcher :setter_method?, '[(
|
17
|
+
def_node_matcher :setter_method?, '[(call ...) setter_method?]'
|
18
18
|
|
19
19
|
# @!method safe_assignment?(node)
|
20
20
|
def_node_matcher :safe_assignment?, '(begin {equals_asgn? #setter_method?})'
|
@@ -51,29 +51,25 @@ module RuboCop
|
|
51
51
|
[Lint::AmbiguousOperator, Style::ArgumentsForwarding]
|
52
52
|
end
|
53
53
|
|
54
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
55
54
|
def on_def(node)
|
56
55
|
return if node.arguments.empty?
|
57
56
|
|
58
57
|
last_argument = node.last_argument
|
59
58
|
return if expected_block_forwarding_style?(node, last_argument)
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
next
|
64
|
-
last_argument.source != block_pass_node.source
|
60
|
+
forwarded_args = node.each_descendant(:block_pass).with_object([]) do |block_pass, result|
|
61
|
+
return nil if invalidates_syntax?(block_pass)
|
62
|
+
next unless block_argument_name_matched?(block_pass, last_argument)
|
65
63
|
|
66
|
-
|
67
|
-
|
68
|
-
next
|
69
|
-
end
|
64
|
+
result << block_pass
|
65
|
+
end
|
70
66
|
|
71
|
-
|
67
|
+
forwarded_args.each do |forwarded_arg|
|
68
|
+
register_offense(forwarded_arg, node)
|
72
69
|
end
|
73
70
|
|
74
|
-
register_offense(last_argument, node)
|
71
|
+
register_offense(last_argument, node)
|
75
72
|
end
|
76
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
77
73
|
alias on_defs on_def
|
78
74
|
|
79
75
|
private
|
@@ -88,6 +84,29 @@ module RuboCop
|
|
88
84
|
end
|
89
85
|
end
|
90
86
|
|
87
|
+
def block_argument_name_matched?(block_pass_node, last_argument)
|
88
|
+
return false if block_pass_node.children.first&.sym_type?
|
89
|
+
|
90
|
+
last_argument.source == block_pass_node.source
|
91
|
+
end
|
92
|
+
|
93
|
+
# Prevents the following syntax error:
|
94
|
+
#
|
95
|
+
# # foo.rb
|
96
|
+
# def foo(&)
|
97
|
+
# block_method do
|
98
|
+
# bar(&)
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# $ ruby -vc foo.rb
|
103
|
+
# ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22]
|
104
|
+
# foo.rb: foo.rb:4: anonymous block parameter is also used within block (SyntaxError)
|
105
|
+
#
|
106
|
+
def invalidates_syntax?(block_pass_node)
|
107
|
+
block_pass_node.each_ancestor(:block, :numblock).any?
|
108
|
+
end
|
109
|
+
|
91
110
|
def use_kwarg_in_method_definition?(node)
|
92
111
|
node.arguments.each_descendant(:kwarg, :kwoptarg).any?
|
93
112
|
end
|
@@ -57,7 +57,7 @@ module RuboCop
|
|
57
57
|
file_path = processed_source.file_path
|
58
58
|
return if config.file_to_exclude?(file_path) || config.allowed_camel_case_file?(file_path)
|
59
59
|
|
60
|
-
for_bad_filename(file_path)
|
60
|
+
for_bad_filename(file_path)
|
61
61
|
end
|
62
62
|
|
63
63
|
private
|
@@ -71,7 +71,7 @@ module RuboCop
|
|
71
71
|
msg = other_message(basename) unless bad_filename_allowed?
|
72
72
|
end
|
73
73
|
|
74
|
-
|
74
|
+
add_global_offense(msg) if msg
|
75
75
|
end
|
76
76
|
|
77
77
|
def perform_class_and_module_naming_checks(file_path, basename)
|
@@ -207,8 +207,7 @@ module RuboCop
|
|
207
207
|
message = create_multiple_word_message_for_file(words)
|
208
208
|
end
|
209
209
|
|
210
|
-
|
211
|
-
add_offense(range, message: message)
|
210
|
+
add_global_offense(message)
|
212
211
|
end
|
213
212
|
|
214
213
|
def create_single_word_message_for_file(word)
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
41
41
|
def on_send(node)
|
42
42
|
return unless node.command?(:alias_method)
|
43
43
|
return unless style == :prefer_alias && alias_keyword_possible?(node)
|
44
|
+
return unless node.arguments.count == 2
|
44
45
|
|
45
46
|
msg = format(MSG_ALIAS_METHOD, current: lexical_scope_type(node))
|
46
47
|
add_offense(node.loc.selector, message: msg) do |corrector|
|
@@ -19,9 +19,7 @@ module RuboCop
|
|
19
19
|
# @example
|
20
20
|
# # bad
|
21
21
|
# array.reject(&:nil?)
|
22
|
-
# array.delete_if(&:nil?)
|
23
22
|
# array.reject { |e| e.nil? }
|
24
|
-
# array.delete_if { |e| e.nil? }
|
25
23
|
# array.select { |e| !e.nil? }
|
26
24
|
# array.grep_v(nil)
|
27
25
|
# array.grep_v(NilClass)
|
@@ -31,7 +29,9 @@ module RuboCop
|
|
31
29
|
#
|
32
30
|
# # bad
|
33
31
|
# hash.reject!(&:nil?)
|
32
|
+
# array.delete_if(&:nil?)
|
34
33
|
# hash.reject! { |k, v| v.nil? }
|
34
|
+
# array.delete_if { |e| e.nil? }
|
35
35
|
# hash.select! { |k, v| !v.nil? }
|
36
36
|
#
|
37
37
|
# # good
|
@@ -127,7 +127,7 @@ module RuboCop
|
|
127
127
|
end
|
128
128
|
|
129
129
|
def good_method_name(node)
|
130
|
-
if node.bang_method?
|
130
|
+
if node.bang_method? || node.method?(:delete_if)
|
131
131
|
'compact!'
|
132
132
|
else
|
133
133
|
'compact'
|
@@ -28,18 +28,27 @@ module RuboCop
|
|
28
28
|
def on_new_investigation
|
29
29
|
return if notice.empty? || notice_found?(processed_source)
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
verify_autocorrect_notice!
|
32
|
+
message = format(MSG, notice: notice)
|
33
|
+
if processed_source.blank?
|
34
|
+
add_global_offense(message)
|
35
|
+
else
|
36
|
+
offense_range = source_range(processed_source.buffer, 1, 0)
|
37
|
+
add_offense(offense_range, message: message) do |corrector|
|
38
|
+
autocorrect(corrector)
|
39
|
+
end
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
43
|
private
|
42
44
|
|
45
|
+
def autocorrect(corrector)
|
46
|
+
token = insert_notice_before(processed_source)
|
47
|
+
range = token.nil? ? range_between(0, 0) : token.pos
|
48
|
+
|
49
|
+
corrector.insert_before(range, "#{autocorrect_notice}\n")
|
50
|
+
end
|
51
|
+
|
43
52
|
def notice
|
44
53
|
cop_config['Notice']
|
45
54
|
end
|
@@ -48,10 +57,6 @@ module RuboCop
|
|
48
57
|
cop_config['AutocorrectNotice']
|
49
58
|
end
|
50
59
|
|
51
|
-
def offense_range
|
52
|
-
source_range(processed_source.buffer, 1, 0)
|
53
|
-
end
|
54
|
-
|
55
60
|
def verify_autocorrect_notice!
|
56
61
|
raise Warning, AUTOCORRECT_EMPTY_WARNING if autocorrect_notice.empty?
|
57
62
|
|
@@ -141,7 +141,7 @@ module RuboCop
|
|
141
141
|
end
|
142
142
|
|
143
143
|
def check_file(node, file_node)
|
144
|
-
return
|
144
|
+
return if special_file_keyword?(file_node)
|
145
145
|
|
146
146
|
message = format(MSG_INCORRECT_FILE,
|
147
147
|
method_name: node.method_name,
|
@@ -155,6 +155,8 @@ module RuboCop
|
|
155
155
|
|
156
156
|
def check_line(node, code)
|
157
157
|
line_node = node.last_argument
|
158
|
+
return if line_node.variable? || (line_node.send_type? && !line_node.method?(:+))
|
159
|
+
|
158
160
|
line_diff = line_difference(line_node, code)
|
159
161
|
if line_diff.zero?
|
160
162
|
add_offense_for_same_line(node, line_node)
|
@@ -38,12 +38,13 @@ module RuboCop
|
|
38
38
|
PATTERN
|
39
39
|
|
40
40
|
def on_send(node)
|
41
|
+
return unless (receiver = node.receiver)
|
41
42
|
return unless (regexp = exact_regexp_match(node))
|
42
43
|
|
43
44
|
parsed_regexp = Regexp::Parser.parse(regexp)
|
44
45
|
return unless exact_match_pattern?(parsed_regexp)
|
45
46
|
|
46
|
-
prefer = "#{
|
47
|
+
prefer = "#{receiver.source} #{new_method(node)} '#{parsed_regexp[1].text}'"
|
47
48
|
|
48
49
|
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
49
50
|
corrector.replace(node, prefer)
|