rubocop 1.62.1 → 1.63.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.
- 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 -4
- data/lib/rubocop/cop/lint/debugger.rb +27 -2
- 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 +2 -0
- 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/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 +34 -4
- 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: 8eadae0a09b33bf6a4ae3621ad1b86ba4eff0e7601d3ceb89dcf115759e9c022
|
4
|
+
data.tar.gz: ae113bb4d3eab63b4a551e36541f14d4e3bf3aae3354b853fb45960e80b90bbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6343643a7515b53e3e880a6cb92e256f37a3c1ee42c757af0506bf6457d900d36ea95b48bef2c6e008b5a96ba957f28b448a8e62da10ef1c27e6fe88dfbc1935
|
7
|
+
data.tar.gz: c67f434f594eb5b3f609d1e36140c04a6c79ce784b2d614c0e57ed4d22f7bea437a4edf89de0b976c425c7a2fcaf54b46ce66391eb9e1b61f60fa03bd46fdebc
|
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,9 +88,7 @@ module RuboCop
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def skip_children?(asgn_node)
|
91
|
-
(asgn_node
|
92
|
-
empty_condition?(asgn_node) ||
|
93
|
-
(safe_assignment_allowed? && safe_assignment?(asgn_node))
|
91
|
+
empty_condition?(asgn_node) || (safe_assignment_allowed? && safe_assignment?(asgn_node))
|
94
92
|
end
|
95
93
|
|
96
94
|
def traverse_node(node, &block)
|
@@ -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?
|
@@ -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
|
|
@@ -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)
|
@@ -25,27 +25,27 @@ module RuboCop
|
|
25
25
|
#
|
26
26
|
# @example EnforcedStyle: format (default)
|
27
27
|
# # bad
|
28
|
-
# puts sprintf('%10s', '
|
29
|
-
# puts '%10s' % '
|
28
|
+
# puts sprintf('%10s', 'foo')
|
29
|
+
# puts '%10s' % 'foo'
|
30
30
|
#
|
31
31
|
# # good
|
32
|
-
# puts format('%10s', '
|
32
|
+
# puts format('%10s', 'foo')
|
33
33
|
#
|
34
34
|
# @example EnforcedStyle: sprintf
|
35
35
|
# # bad
|
36
|
-
# puts format('%10s', '
|
37
|
-
# puts '%10s' % '
|
36
|
+
# puts format('%10s', 'foo')
|
37
|
+
# puts '%10s' % 'foo'
|
38
38
|
#
|
39
39
|
# # good
|
40
|
-
# puts sprintf('%10s', '
|
40
|
+
# puts sprintf('%10s', 'foo')
|
41
41
|
#
|
42
42
|
# @example EnforcedStyle: percent
|
43
43
|
# # bad
|
44
|
-
# puts format('%10s', '
|
45
|
-
# puts sprintf('%10s', '
|
44
|
+
# puts format('%10s', 'foo')
|
45
|
+
# puts sprintf('%10s', 'foo')
|
46
46
|
#
|
47
47
|
# # good
|
48
|
-
# puts '%10s' % '
|
48
|
+
# puts '%10s' % 'foo'
|
49
49
|
#
|
50
50
|
class FormatString < Base
|
51
51
|
include ConfigurableEnforcedStyle
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for usages of `each` with `<<`, `push`, or `append` which
|
7
|
+
# can be replaced by `map`.
|
8
|
+
#
|
9
|
+
# If `PreferredMethods` is configured for `map` in `Style/CollectionMethods`,
|
10
|
+
# this cop uses the specified method for replacement.
|
11
|
+
#
|
12
|
+
# NOTE: The return value of `Enumerable#each` is `self`, whereas the
|
13
|
+
# return value of `Enumerable#map` is an `Array`. They are not autocorrected
|
14
|
+
# when a return value could be used because these types differ.
|
15
|
+
#
|
16
|
+
# NOTE: It only detects when the mapping destination is a local variable
|
17
|
+
# initialized as an empty array and referred to only by the pushing operation.
|
18
|
+
# This is because, if not, it's challenging to statically guarantee that the
|
19
|
+
# mapping destination variable remains an empty array:
|
20
|
+
#
|
21
|
+
# [source,ruby]
|
22
|
+
# ----
|
23
|
+
# @dest = []
|
24
|
+
# src.each { |e| @dest << e * 2 } # `src` method may mutate `@dest`
|
25
|
+
#
|
26
|
+
# dest = []
|
27
|
+
# src.each { |e| dest << transform(e, dest) } # `transform` method may mutate `dest`
|
28
|
+
# ----
|
29
|
+
#
|
30
|
+
# @safety
|
31
|
+
# This cop is unsafe because not all objects that have an `each`
|
32
|
+
# method also have a `map` method (e.g. `ENV`). Additionally, for calls
|
33
|
+
# with a block, not all objects that have a `map` method return an array
|
34
|
+
# (e.g. `Enumerator::Lazy`).
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# # bad
|
38
|
+
# dest = []
|
39
|
+
# src.each { |e| dest << e * 2 }
|
40
|
+
# dest
|
41
|
+
#
|
42
|
+
# # good
|
43
|
+
# dest = src.map { |e| e * 2 }
|
44
|
+
#
|
45
|
+
# # good - contains another operation
|
46
|
+
# dest = []
|
47
|
+
# src.each { |e| dest << e * 2; puts e }
|
48
|
+
# dest
|
49
|
+
#
|
50
|
+
class MapIntoArray < Base
|
51
|
+
include RangeHelp
|
52
|
+
extend AutoCorrector
|
53
|
+
|
54
|
+
MSG = 'Use `%<new_method_name>s` instead of `each` to map elements into an array.'
|
55
|
+
|
56
|
+
# @!method each_block_with_push?(node)
|
57
|
+
def_node_matcher :each_block_with_push?, <<-PATTERN
|
58
|
+
[
|
59
|
+
^({begin kwbegin} ...)
|
60
|
+
({block numblock} (send _ :each) _
|
61
|
+
(send (lvar _) {:<< :push :append} _))
|
62
|
+
]
|
63
|
+
PATTERN
|
64
|
+
|
65
|
+
# @!method empty_array_asgn?(node)
|
66
|
+
def_node_matcher :empty_array_asgn?, '(lvasgn _ (array))'
|
67
|
+
|
68
|
+
# @!method lvar_ref?(node, name)
|
69
|
+
def_node_matcher :lvar_ref?, '(lvar %1)'
|
70
|
+
|
71
|
+
def self.joining_forces
|
72
|
+
VariableForce
|
73
|
+
end
|
74
|
+
|
75
|
+
def after_leaving_scope(scope, _variable_table)
|
76
|
+
(@scopes ||= []) << scope
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_block(node)
|
80
|
+
return unless each_block_with_push?(node)
|
81
|
+
|
82
|
+
dest_var = find_dest_var(node)
|
83
|
+
return unless (asgn = find_closest_assignment(node, dest_var))
|
84
|
+
return unless empty_array_asgn?(asgn)
|
85
|
+
return unless dest_used_only_for_mapping?(node, dest_var, asgn)
|
86
|
+
|
87
|
+
register_offense(node, dest_var, asgn)
|
88
|
+
end
|
89
|
+
|
90
|
+
alias on_numblock on_block
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def find_dest_var(block)
|
95
|
+
node = block.body.receiver
|
96
|
+
name = node.children.first
|
97
|
+
|
98
|
+
candidates = @scopes.lazy.filter_map { |s| s.variables[name] }
|
99
|
+
candidates.find { |v| v.references.any? { |n| n.node.equal?(node) } }
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_closest_assignment(block, dest_var)
|
103
|
+
dest_var.assignments.reverse_each.lazy.map(&:node).find do |node|
|
104
|
+
node.source_range.end_pos < block.source_range.begin_pos
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def dest_used_only_for_mapping?(block, dest_var, asgn)
|
109
|
+
range = asgn.source_range.join(block.source_range)
|
110
|
+
|
111
|
+
asgn.parent.equal?(block.parent) &&
|
112
|
+
dest_var.references.one? { |r| range.contains?(r.node.source_range) } &&
|
113
|
+
dest_var.assignments.one? { |a| range.contains?(a.node.source_range) }
|
114
|
+
end
|
115
|
+
|
116
|
+
def register_offense(block, dest_var, asgn)
|
117
|
+
add_offense(block, message: format(MSG, new_method_name: new_method_name)) do |corrector|
|
118
|
+
next if return_value_used?(block)
|
119
|
+
|
120
|
+
corrector.replace(block.send_node.selector, new_method_name)
|
121
|
+
remove_assignment(corrector, asgn)
|
122
|
+
correct_push_node(corrector, block.body)
|
123
|
+
correct_return_value_handling(corrector, block, dest_var)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def new_method_name
|
128
|
+
default = 'map'
|
129
|
+
alternative = config.for_cop('Style/CollectionMethods').dig('PreferredMethods', default)
|
130
|
+
alternative || default
|
131
|
+
end
|
132
|
+
|
133
|
+
def return_value_used?(node)
|
134
|
+
parent = node.parent
|
135
|
+
|
136
|
+
case parent&.type
|
137
|
+
when nil
|
138
|
+
false
|
139
|
+
when :begin, :kwbegin
|
140
|
+
!node.right_sibling && return_value_used?(parent)
|
141
|
+
when :block, :numblock
|
142
|
+
!parent.void_context?
|
143
|
+
else
|
144
|
+
true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def remove_assignment(corrector, asgn)
|
149
|
+
range = range_with_surrounding_space(asgn.source_range, side: :right)
|
150
|
+
range = range_with_surrounding_space(range, side: :right, newlines: false)
|
151
|
+
|
152
|
+
corrector.remove(range)
|
153
|
+
end
|
154
|
+
|
155
|
+
def correct_push_node(corrector, push_node)
|
156
|
+
range = push_node.source_range
|
157
|
+
arg_range = push_node.first_argument.source_range
|
158
|
+
|
159
|
+
corrector.remove(range_between(range.begin_pos, arg_range.begin_pos))
|
160
|
+
corrector.remove(range_between(arg_range.end_pos, range.end_pos))
|
161
|
+
end
|
162
|
+
|
163
|
+
def correct_return_value_handling(corrector, block, dest_var)
|
164
|
+
next_node = block.right_sibling
|
165
|
+
|
166
|
+
if lvar_ref?(next_node, dest_var.name)
|
167
|
+
corrector.remove(range_with_surrounding_space(next_node.source_range, side: :left))
|
168
|
+
end
|
169
|
+
|
170
|
+
corrector.insert_before(block, "#{dest_var.name} = ")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
55
55
|
message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
|
56
56
|
add_offense(map_node.loc.selector, message: message) do |corrector|
|
57
57
|
# If the `to_h` call already has a block, do not autocorrect.
|
58
|
-
next if to_h_node.
|
58
|
+
next if to_h_node.block_literal?
|
59
59
|
|
60
60
|
autocorrect(corrector, to_h_node, map_node)
|
61
61
|
end
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
44
44
|
message = format(MSG, method: map_node.loc.selector.source)
|
45
45
|
add_offense(map_node.loc.selector, message: message) do |corrector|
|
46
46
|
# If the `to_set` call already has a block, do not autocorrect.
|
47
|
-
next if to_set_node.
|
47
|
+
next if to_set_node.block_literal?
|
48
48
|
|
49
49
|
autocorrect(corrector, to_set_node, map_node)
|
50
50
|
end
|
@@ -81,7 +81,13 @@ module RuboCop
|
|
81
81
|
redundant_argument = redundant_arg_for_method(node.method_name.to_s)
|
82
82
|
return false if redundant_argument.nil?
|
83
83
|
|
84
|
-
node.first_argument.
|
84
|
+
target_argument = if node.first_argument.respond_to?(:value)
|
85
|
+
node.first_argument.value
|
86
|
+
else
|
87
|
+
node.first_argument
|
88
|
+
end
|
89
|
+
|
90
|
+
argument_matched?(target_argument, redundant_argument)
|
85
91
|
end
|
86
92
|
|
87
93
|
def redundant_arg_for_method(method_name)
|
@@ -98,6 +104,23 @@ module RuboCop
|
|
98
104
|
range_with_surrounding_space(node.first_argument.source_range, newlines: false)
|
99
105
|
end
|
100
106
|
end
|
107
|
+
|
108
|
+
def argument_matched?(target_argument, redundant_argument)
|
109
|
+
argument = if target_argument.is_a?(AST::Node)
|
110
|
+
target_argument.source
|
111
|
+
elsif exclude_cntrl_character?(target_argument, redundant_argument)
|
112
|
+
target_argument.inspect
|
113
|
+
else
|
114
|
+
target_argument.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
argument == redundant_argument
|
118
|
+
end
|
119
|
+
|
120
|
+
def exclude_cntrl_character?(target_argument, redundant_argument)
|
121
|
+
!target_argument.to_s.sub(/\A'/, '"').sub(/'\z/, '"').match?(/[[:cntrl:]]/) ||
|
122
|
+
!redundant_argument.match?(/[[:cntrl:]]/)
|
123
|
+
end
|
101
124
|
end
|
102
125
|
end
|
103
126
|
end
|
@@ -18,10 +18,10 @@ module RuboCop
|
|
18
18
|
extend AutoCorrector
|
19
19
|
|
20
20
|
MSG = 'Remove the redundant current directory path.'
|
21
|
+
RESTRICT_ON_SEND = %i[require_relative].freeze
|
21
22
|
CURRENT_DIRECTORY_PATH = './'
|
22
23
|
|
23
24
|
def on_send(node)
|
24
|
-
return unless node.method?(:require_relative)
|
25
25
|
return unless (first_argument = node.first_argument)
|
26
26
|
return unless first_argument.str_content&.start_with?(CURRENT_DIRECTORY_PATH)
|
27
27
|
return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PATH))
|
@@ -79,7 +79,7 @@ module RuboCop
|
|
79
79
|
private_constant :REPLACEMENT_METHODS
|
80
80
|
|
81
81
|
def on_send(node)
|
82
|
-
return if node.arguments? || node.
|
82
|
+
return if node.arguments? || node.block_literal?
|
83
83
|
|
84
84
|
select_predicate?(node) do |select_node, filter_method|
|
85
85
|
return if RAILS_METHODS.include?(filter_method) && !active_support_extensions_enabled?
|
@@ -72,7 +72,7 @@ module RuboCop
|
|
72
72
|
ALLOWED_STRING_TOKENS = %i[tSTRING tSTRING_CONTENT].freeze
|
73
73
|
ARGUMENT_TYPES = %i[
|
74
74
|
kFALSE kNIL kSELF kTRUE tCONSTANT tCVAR tFLOAT tGVAR tIDENTIFIER tINTEGER tIVAR
|
75
|
-
|
75
|
+
tLBRACK tLCURLY tLPAREN_ARG tSTRING tSTRING_BEG tSYMBOL tXSTRING_BEG
|
76
76
|
].freeze
|
77
77
|
|
78
78
|
def on_new_investigation
|
@@ -124,10 +124,8 @@ module RuboCop
|
|
124
124
|
return true unless (node = find_node_for_line(range.line))
|
125
125
|
return false if argument_newline?(node)
|
126
126
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
continuation_node.source.include?("\n") || continuation_node.source.include?("\\\n")
|
127
|
+
source = node.parent ? node.parent.source : node.source
|
128
|
+
parse(source.gsub("\\\n", "\n")).valid_syntax?
|
131
129
|
end
|
132
130
|
|
133
131
|
def inside_string_literal?(range, token)
|
@@ -142,22 +140,22 @@ module RuboCop
|
|
142
140
|
current_token.type == :tIDENTIFIER && ARGUMENT_TYPES.include?(next_token.type)
|
143
141
|
end
|
144
142
|
|
145
|
-
# rubocop:disable Metrics/AbcSize
|
143
|
+
# rubocop:disable Metrics/AbcSize
|
146
144
|
def argument_newline?(node)
|
147
145
|
node = node.to_a.last if node.assignment?
|
148
146
|
return false if node.parenthesized_call?
|
149
147
|
|
150
148
|
node = node.children.first if node.root? && node.begin_type?
|
151
149
|
|
152
|
-
if argument_is_method?(node)
|
153
|
-
argument_newline?(node.
|
150
|
+
if argument_is_method?(node)
|
151
|
+
argument_newline?(node.first_argument)
|
154
152
|
else
|
155
153
|
return false unless method_call_with_arguments?(node)
|
156
154
|
|
157
|
-
|
155
|
+
node.loc.selector.line != node.first_argument.loc.line
|
158
156
|
end
|
159
157
|
end
|
160
|
-
# rubocop:enable Metrics/AbcSize
|
158
|
+
# rubocop:enable Metrics/AbcSize
|
161
159
|
|
162
160
|
def find_node_for_line(line)
|
163
161
|
processed_source.ast.each_node do |node|
|
@@ -165,10 +163,6 @@ module RuboCop
|
|
165
163
|
end
|
166
164
|
end
|
167
165
|
|
168
|
-
def allowed_type?(node)
|
169
|
-
node.and_type? || node.or_type? || (node.if_type? && node.ternary?)
|
170
|
-
end
|
171
|
-
|
172
166
|
def same_line?(node, line)
|
173
167
|
return false unless (source_range = node.source_range)
|
174
168
|
|
@@ -53,7 +53,7 @@ module RuboCop
|
|
53
53
|
return if interpolated_quotes?(node) || allowed_percent_q?(node)
|
54
54
|
|
55
55
|
add_offense(node) do |corrector|
|
56
|
-
delimiter =
|
56
|
+
delimiter = /\A%Q[^"]+\z|'/.match?(node.source) ? QUOTE : SINGLE_QUOTE
|
57
57
|
|
58
58
|
corrector.replace(node.loc.begin, delimiter)
|
59
59
|
corrector.replace(node.loc.end, delimiter)
|
data/lib/rubocop/cop/team.rb
CHANGED
@@ -174,6 +174,9 @@ module RuboCop
|
|
174
174
|
end
|
175
175
|
|
176
176
|
def support_target_rails_version?(cop)
|
177
|
+
# In this case, the rails version was already checked by `#excluded_file?`
|
178
|
+
return true if defined?(RuboCop::Rails::TargetRailsVersion::USES_REQUIRES_GEM_API)
|
179
|
+
|
177
180
|
return true unless cop.class.respond_to?(:support_target_rails_version?)
|
178
181
|
|
179
182
|
cop.class.support_target_rails_version?(cop.target_rails_version)
|
@@ -24,14 +24,10 @@ module RuboCop
|
|
24
24
|
message: message(offense)
|
25
25
|
)
|
26
26
|
|
27
|
-
|
28
|
-
return unless valid_line?(offense)
|
27
|
+
return unless valid_line?(offense)
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
rescue IndexError
|
33
|
-
# range is not on a valid line; perhaps the source file is empty
|
34
|
-
end
|
29
|
+
report_line(offense.location)
|
30
|
+
report_highlighted_area(offense.highlighted_area)
|
35
31
|
end
|
36
32
|
|
37
33
|
def valid_line?(offense)
|
@@ -53,14 +53,10 @@ module RuboCop
|
|
53
53
|
message: message(offense)
|
54
54
|
)
|
55
55
|
|
56
|
-
|
57
|
-
return unless valid_line?(offense)
|
56
|
+
return unless valid_line?(offense)
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
rescue IndexError
|
62
|
-
# range is not on a valid line; perhaps the source file is empty
|
63
|
-
end
|
58
|
+
report_line(offense.location)
|
59
|
+
report_highlighted_area(offense.highlighted_area)
|
64
60
|
end
|
65
61
|
|
66
62
|
def annotate_message(msg)
|
data/lib/rubocop/lockfile.rb
CHANGED
@@ -5,14 +5,23 @@ module RuboCop
|
|
5
5
|
# Does not actually resolve gems, just parses the lockfile.
|
6
6
|
# @api private
|
7
7
|
class Lockfile
|
8
|
-
#
|
8
|
+
# @param [String, Pathname, nil] lockfile_path
|
9
|
+
def initialize(lockfile_path = nil)
|
10
|
+
lockfile_path ||= defined?(Bundler) ? Bundler.default_lockfile : nil
|
11
|
+
|
12
|
+
@lockfile_path = lockfile_path
|
13
|
+
end
|
14
|
+
|
15
|
+
# Gems that the bundle directly depends on.
|
16
|
+
# @return [Array<Bundler::Dependency>, nil]
|
9
17
|
def dependencies
|
10
18
|
return [] unless parser
|
11
19
|
|
12
20
|
parser.dependencies.values
|
13
21
|
end
|
14
22
|
|
15
|
-
# All activated gems, including transitive dependencies
|
23
|
+
# All activated gems, including transitive dependencies.
|
24
|
+
# @return [Array<Bundler::Dependency>, nil]
|
16
25
|
def gems
|
17
26
|
return [] unless parser
|
18
27
|
|
@@ -21,17 +30,38 @@ module RuboCop
|
|
21
30
|
parser.dependencies.values.concat(parser.specs.flat_map(&:dependencies))
|
22
31
|
end
|
23
32
|
|
33
|
+
# Returns the locked versions of gems from this lockfile.
|
34
|
+
# @param [Boolean] include_transitive_dependencies: When false, only direct dependencies
|
35
|
+
# are returned, i.e. those listed explicitly in the `Gemfile`.
|
36
|
+
# @returns [Hash{String => Gem::Version}] The locked gem versions, keyed by the gems' names.
|
37
|
+
def gem_versions(include_transitive_dependencies: true)
|
38
|
+
return {} unless parser
|
39
|
+
|
40
|
+
all_gem_versions = parser.specs.to_h { |spec| [spec.name, spec.version] }
|
41
|
+
|
42
|
+
if include_transitive_dependencies
|
43
|
+
all_gem_versions
|
44
|
+
else
|
45
|
+
direct_dep_names = parser.dependencies.keys
|
46
|
+
all_gem_versions.slice(*direct_dep_names)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Whether this lockfile includes the named gem, directly or indirectly.
|
51
|
+
# @param [String] name
|
52
|
+
# @return [Boolean]
|
24
53
|
def includes_gem?(name)
|
25
54
|
gems.any? { |gem| gem.name == name }
|
26
55
|
end
|
27
56
|
|
28
57
|
private
|
29
58
|
|
59
|
+
# @return [Bundler::LockfileParser, nil]
|
30
60
|
def parser
|
31
|
-
return unless defined?(Bundler) && Bundler.default_lockfile
|
32
61
|
return @parser if defined?(@parser)
|
62
|
+
return unless @lockfile_path
|
33
63
|
|
34
|
-
lockfile = Bundler.read_file(
|
64
|
+
lockfile = Bundler.read_file(@lockfile_path)
|
35
65
|
@parser = lockfile ? Bundler::LockfileParser.new(lockfile) : nil
|
36
66
|
rescue Bundler::BundlerError
|
37
67
|
nil
|
@@ -111,6 +111,7 @@ module RuboCop
|
|
111
111
|
source
|
112
112
|
end
|
113
113
|
|
114
|
+
# rubocop:disable Metrics/AbcSize
|
114
115
|
def expect_offense(source, file = nil, severity: nil, chomp: false, **replacements)
|
115
116
|
expected_annotations = parse_annotations(source, **replacements)
|
116
117
|
source = expected_annotations.plain_source
|
@@ -123,8 +124,15 @@ module RuboCop
|
|
123
124
|
expect(actual_annotations).to eq(expected_annotations), ''
|
124
125
|
expect(@offenses.map(&:severity).uniq).to eq([severity]) if severity
|
125
126
|
|
127
|
+
# Validate that all offenses have a range that formatters can display
|
128
|
+
expect do
|
129
|
+
@offenses.each { |offense| offense.location.source_line }
|
130
|
+
end.not_to raise_error, 'One of the offenses has a misconstructed range, for ' \
|
131
|
+
'example if the offense is on line 1 and the source is empty'
|
132
|
+
|
126
133
|
@offenses
|
127
134
|
end
|
135
|
+
# rubocop:enable Metrics/AbcSize
|
128
136
|
|
129
137
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
130
138
|
def expect_correction(correction, loop: true, source: nil)
|
@@ -110,7 +110,19 @@ RSpec.shared_context 'config' do # rubocop:disable Metrics/BlockLength
|
|
110
110
|
let(:config) do
|
111
111
|
hash = { 'AllCops' => all_cops_config, cop_class.cop_name => cur_cop_config }.merge!(other_cops)
|
112
112
|
|
113
|
-
RuboCop::Config.new(hash, "#{Dir.pwd}/.rubocop.yml")
|
113
|
+
config = RuboCop::Config.new(hash, "#{Dir.pwd}/.rubocop.yml")
|
114
|
+
|
115
|
+
rails_version_in_gemfile = Gem::Version.new(
|
116
|
+
rails_version || RuboCop::Config::DEFAULT_RAILS_VERSION
|
117
|
+
)
|
118
|
+
|
119
|
+
allow(config).to receive(:gem_versions_in_target).and_return(
|
120
|
+
{
|
121
|
+
'railties' => rails_version_in_gemfile
|
122
|
+
}
|
123
|
+
)
|
124
|
+
|
125
|
+
config
|
114
126
|
end
|
115
127
|
|
116
128
|
let(:cop) { cop_class.new(config, cop_options) }
|
data/lib/rubocop/runner.rb
CHANGED
@@ -20,6 +20,9 @@ module RuboCop
|
|
20
20
|
message = 'Infinite loop detected'
|
21
21
|
message += " in #{path}" if path
|
22
22
|
message += " and caused by #{root_cause}" if root_cause
|
23
|
+
message += ' Hint: Please update to the latest RuboCop version if not already in use,'
|
24
|
+
message += ' and report a bug if the issue still occurs on this version.'
|
25
|
+
message += ' Please check the latest version at https://rubygems.org/gems/rubocop'
|
23
26
|
super(message)
|
24
27
|
end
|
25
28
|
end
|
data/lib/rubocop/version.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
# This module holds the RuboCop version information.
|
5
5
|
module Version
|
6
|
-
STRING = '1.
|
6
|
+
STRING = '1.63.0'
|
7
7
|
|
8
8
|
MSG = '%<version>s (using %<parser_version>s, ' \
|
9
9
|
'rubocop-ast %<rubocop_ast_version>s, ' \
|
@@ -11,7 +11,7 @@ module RuboCop
|
|
11
11
|
|
12
12
|
CANONICAL_FEATURE_NAMES = {
|
13
13
|
'Rspec' => 'RSpec', 'Graphql' => 'GraphQL', 'Md' => 'Markdown', 'Factory_bot' => 'FactoryBot',
|
14
|
-
'Thread_safety' => 'ThreadSafety'
|
14
|
+
'Thread_safety' => 'ThreadSafety', 'Rspec_rails' => 'RSpecRails'
|
15
15
|
}.freeze
|
16
16
|
EXTENSION_PATH_NAMES = {
|
17
17
|
'rubocop-md' => 'markdown', 'rubocop-factory_bot' => 'factory_bot'
|
data/lib/rubocop.rb
CHANGED
@@ -557,6 +557,7 @@ require_relative 'rubocop/cop/style/lambda'
|
|
557
557
|
require_relative 'rubocop/cop/style/lambda_call'
|
558
558
|
require_relative 'rubocop/cop/style/line_end_concatenation'
|
559
559
|
require_relative 'rubocop/cop/style/magic_comment_format'
|
560
|
+
require_relative 'rubocop/cop/style/map_into_array'
|
560
561
|
require_relative 'rubocop/cop/style/map_to_hash'
|
561
562
|
require_relative 'rubocop/cop/style/map_to_set'
|
562
563
|
require_relative 'rubocop/cop/style/method_call_without_args_parentheses'
|
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: 1.
|
4
|
+
version: 1.63.0
|
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: 2024-
|
13
|
+
date: 2024-04-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json
|
@@ -778,6 +778,7 @@ files:
|
|
778
778
|
- lib/rubocop/cop/style/line_end_concatenation.rb
|
779
779
|
- lib/rubocop/cop/style/magic_comment_format.rb
|
780
780
|
- lib/rubocop/cop/style/map_compact_with_conditional_block.rb
|
781
|
+
- lib/rubocop/cop/style/map_into_array.rb
|
781
782
|
- lib/rubocop/cop/style/map_to_hash.rb
|
782
783
|
- lib/rubocop/cop/style/map_to_set.rb
|
783
784
|
- lib/rubocop/cop/style/method_call_with_args_parentheses.rb
|
@@ -1031,9 +1032,9 @@ licenses:
|
|
1031
1032
|
- MIT
|
1032
1033
|
metadata:
|
1033
1034
|
homepage_uri: https://rubocop.org/
|
1034
|
-
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.
|
1035
|
+
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.63.0
|
1035
1036
|
source_code_uri: https://github.com/rubocop/rubocop/
|
1036
|
-
documentation_uri: https://docs.rubocop.org/rubocop/1.
|
1037
|
+
documentation_uri: https://docs.rubocop.org/rubocop/1.63/
|
1037
1038
|
bug_tracker_uri: https://github.com/rubocop/rubocop/issues
|
1038
1039
|
rubygems_mfa_required: 'true'
|
1039
1040
|
post_install_message:
|