rubocop-vicenzo 0.8.0 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '094cfb18e36f28e2f311e80f435b1b5159f22875cfb8f13b5a0551ca9b5e9218'
4
- data.tar.gz: b96a9634c956e7cec399e10a2f7f512a5f2894822ba07fecf7294a502fee56b5
3
+ metadata.gz: 4f7a8f58f53c6f2e6b50f1645a55cb401cd33b1f2654c793eaecd52cbf879153
4
+ data.tar.gz: f2eaba7a6bbb615190abf109a64681372988e3f8cea255c6055f6ae40698c7e2
5
5
  SHA512:
6
- metadata.gz: ba554984ec7092154352415043f2868cd59bd89015de29b0098e01c1f0b2016b8b3a837845cd22e8219e1115b37f4b6e80eb3d5861d0f3325c0ad2894466de23
7
- data.tar.gz: 031a968654d68f7fa65e7a57a6178fc97651ae8c20cb4861f25a1f4c9feb5c6d044a3bc7e269e4de392a9a38483ce51296517f852abe61e88ba804ad723140b4
6
+ metadata.gz: '0258853f6e8d32fb88d7d4c53fb5520e2c828bd31d5968be91008ab34c13a729188c802e91539ef95eef937c56dc584cb59c7dbb9ddcc8c50e3cd6e991eb49a1'
7
+ data.tar.gz: c8a4a8d41af0c5d5f294acfd9361f1226f002590b051508b97a6fbcb9f3f36dbe695a6ae6efbccb7c122395bc9dd76e83f7cffc9a026f3d822954e20236483df
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.8.0"
2
+ ".": "0.10.0"
3
3
  }
data/.rubocop.yml CHANGED
@@ -1,3 +1,5 @@
1
+ inherit_from: config/style.yml
2
+
1
3
  plugins:
2
4
  - rubocop-performance
3
5
  - rubocop-rake
@@ -5,39 +7,17 @@ plugins:
5
7
  - rubocop-vicenzo
6
8
  - rubocop-internal_affairs
7
9
 
8
- AllCops:
9
- Exclude:
10
- - 'bin/**/*'
11
- - 'vendor/**/*'
12
- NewCops: enable
13
-
14
10
  InternalAffairs/UndefinedConfig: # False positive because conflicts with Rubocop Layout namespace
15
11
  Exclude:
16
12
  - 'lib/rubocop/cop/vicenzo/layout/**/*'
17
13
 
18
- Metrics/BlockLength:
19
- Exclude:
20
- - 'rakelib/**/*'
21
- - 'Rakefile'
22
-
23
14
  Naming/FileName:
24
15
  Exclude:
25
16
  - lib/rubocop-vicenzo.rb
26
17
 
27
- RSpec/ContextWording:
28
- Prefixes:
29
- - and
30
- - but
31
- - when
32
- - with
33
- - without
34
-
35
18
  RSpec/ExampleLength:
36
19
  Enabled: false
37
20
 
38
- RSpec/NestedGroups:
39
- Enabled: false
40
-
41
21
  Vicenzo/RSpec/LeakyDefinition:
42
22
  Exclude:
43
23
  - 'spec/support/**/*'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.0](https://github.com/bvicenzo/rubocop-vicenzo/compare/v0.9.0...v0.10.0) (2026-10-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * add Vicenzo/RSpec/LetAliasingSubject cop ([#44](https://github.com/bvicenzo/rubocop-vicenzo/issues/44)) ([a8cebd3](https://github.com/bvicenzo/rubocop-vicenzo/commit/a8cebd3c0237e8805a58ff9efa7ca34f6beb34b0)), closes [#42](https://github.com/bvicenzo/rubocop-vicenzo/issues/42)
9
+ * add Vicenzo/Style/PreferManyOverSizeComparison cop ([#38](https://github.com/bvicenzo/rubocop-vicenzo/issues/38)) ([910ea02](https://github.com/bvicenzo/rubocop-vicenzo/commit/910ea026f7e4c46f9de27db580e6330cf845bfe7))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * report subjects that derive from an allowed method in SubjectIsMethodResult ([#41](https://github.com/bvicenzo/rubocop-vicenzo/issues/41)) ([a5efd33](https://github.com/bvicenzo/rubocop-vicenzo/commit/a5efd3330b32dd24b4291121a64e378091297a57)), closes [#40](https://github.com/bvicenzo/rubocop-vicenzo/issues/40)
15
+ * see subjects declared in shared groups in LetIsMethodResult and LetAliasingSubject ([#45](https://github.com/bvicenzo/rubocop-vicenzo/issues/45)) ([a98039e](https://github.com/bvicenzo/rubocop-vicenzo/commit/a98039e2cbea568267d8b7832b4717b763678d14)), closes [#43](https://github.com/bvicenzo/rubocop-vicenzo/issues/43)
16
+
17
+ ## [0.9.0](https://github.com/bvicenzo/rubocop-vicenzo/compare/v0.8.0...v0.9.0) (2026-09-23)
18
+
19
+
20
+ ### Features
21
+
22
+ * add Vicenzo/Naming/AnonymousUnusedName cop ([#33](https://github.com/bvicenzo/rubocop-vicenzo/issues/33)) ([fabbad6](https://github.com/bvicenzo/rubocop-vicenzo/commit/fabbad655dac0c362924dd0aaf245abd2c7023e1))
23
+ * add Vicenzo/Naming/ShortName cop and an opinionated config/style.yml ([#35](https://github.com/bvicenzo/rubocop-vicenzo/issues/35)) ([1b67f46](https://github.com/bvicenzo/rubocop-vicenzo/commit/1b67f4643b4348f5768a8fbb3b88cea35046e5e2))
24
+ * add Vicenzo/RSpec/DescribeInsideContext cop ([#37](https://github.com/bvicenzo/rubocop-vicenzo/issues/37)) ([3f3d6ae](https://github.com/bvicenzo/rubocop-vicenzo/commit/3f3d6aeb95d087e32594e8ded3d1ce0e3715dee7))
25
+ * add Vicenzo/RSpec/UnnestedContextImproperStart cop ([#36](https://github.com/bvicenzo/rubocop-vicenzo/issues/36)) ([0a8fb0f](https://github.com/bvicenzo/rubocop-vicenzo/commit/0a8fb0fec62370ac331b9a3275b95a0b22998b1d))
26
+
3
27
  ## [0.8.0](https://github.com/bvicenzo/rubocop-vicenzo/compare/v0.7.0...v0.8.0) (2026-09-09)
4
28
 
5
29
 
data/README.md CHANGED
@@ -11,7 +11,7 @@ Add it to the `development`/`test` group of your `Gemfile`:
11
11
  <!-- x-release-please-start-version -->
12
12
  ```ruby
13
13
  group :development, :test do
14
- gem 'rubocop-vicenzo', '~> 0.8.0', require: false
14
+ gem 'rubocop-vicenzo', '~> 0.10.0', require: false
15
15
  end
16
16
  ```
17
17
  <!-- x-release-please-end-version -->
@@ -64,6 +64,31 @@ cops together with the standard cops.
64
64
  rubocop --plugin rubocop-vicenzo
65
65
  ```
66
66
 
67
+ ## Opinionated style guide
68
+
69
+ Loading the plugin gives you the `Vicenzo/*` cops and nothing else: their defaults
70
+ live in `config/default.yml` and are applied automatically.
71
+
72
+ The gem also ships `config/style.yml`, an opinionated configuration of the cops the
73
+ `Vicenzo/*` ones build on — line length, multiline layout, metrics, and the naming
74
+ rules that back `Vicenzo/Naming/ShortName` (notably `Naming/BlockParameterName`,
75
+ which RuboCop ships with `MinNameLength: 1` and which therefore never fires). It is
76
+ **opt-in**:
77
+
78
+ ```yaml
79
+ inherit_gem:
80
+ rubocop-vicenzo: config/style.yml
81
+ ```
82
+
83
+ That single line brings the configuration **and** the plugins it builds on
84
+ (`rubocop-rspec` and this gem's own cops), so there is no `plugins:` key left for you
85
+ to write.
86
+
87
+ > [!NOTE]
88
+ > `AllCops: CacheRootDirectory` is not inheritable. RuboCop reads it from the
89
+ > top-level config file before resolving `inherit_from`/`inherit_gem`, so it has to
90
+ > stay in your own `.rubocop.yml`.
91
+
67
92
  ## Development
68
93
 
69
94
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/Rakefile CHANGED
@@ -4,8 +4,8 @@ require 'bundler/setup'
4
4
  require 'bundler/gem_tasks'
5
5
  require 'rspec/core/rake_task'
6
6
 
7
- RSpec::Core::RakeTask.new(:spec) do |t|
8
- t.rspec_opts = '--format progress'
7
+ RSpec::Core::RakeTask.new(:spec) do |rspec_task|
8
+ rspec_task.rspec_opts = '--format progress'
9
9
  end
10
10
 
11
11
  require 'rubocop/rake_task'
data/config/default.yml CHANGED
@@ -5,6 +5,19 @@ Vicenzo/Layout/MultilineMethodCallLineBreaks:
5
5
  IndentationWidth: 2
6
6
  VersionAdded: '0.3.0'
7
7
 
8
+ Vicenzo/Naming/AnonymousUnusedName:
9
+ Description: 'An unused declaration must not be anonymous; name it after what it stands for.'
10
+ Enabled: true
11
+ Severity: convention
12
+ VersionAdded: '0.9.0'
13
+
14
+ Vicenzo/Naming/ShortName:
15
+ Description: 'A name too short to carry meaning; spell out whole words from the domain.'
16
+ Enabled: true
17
+ Severity: convention
18
+ MinNameLength: 3
19
+ VersionAdded: '0.9.0'
20
+
8
21
  Vicenzo/Rails/EnumInclusionOfValidation:
9
22
  Description: 'Check if the enum has the inclusion of validation defined.'
10
23
  Enabled: true
@@ -45,6 +58,17 @@ Vicenzo/RSpec/DerivedPremises:
45
58
  - '**/spec/factories/**/*'
46
59
  VersionAdded: '0.6.0'
47
60
 
61
+ Vicenzo/RSpec/DescribeInsideContext:
62
+ Description: 'A describe must not be nested in a context, which is a scenario of the behaviour a describe names.'
63
+ Enabled: true
64
+ Severity: warning
65
+ Include:
66
+ - '**/spec/**/*_spec.rb'
67
+ Exclude:
68
+ - '**/spec/support/**/*'
69
+ - '**/spec/factories/**/*'
70
+ VersionAdded: '0.9.0'
71
+
48
72
  Vicenzo/RSpec/DynamicExampleGeneration:
49
73
  Description: 'Do not use iteration to dynamically generate example groups or examples.'
50
74
  Enabled: true
@@ -103,6 +127,17 @@ Vicenzo/RSpec/LeakyDefinition:
103
127
  - '**/spec/support/**/*'
104
128
  - '**/spec/factories/**/*'
105
129
 
130
+ Vicenzo/RSpec/LetAliasingSubject:
131
+ Description: 'A let must not hold the subject itself under another name.'
132
+ Enabled: true
133
+ Severity: convention
134
+ Include:
135
+ - '**/spec/**/*_spec.rb'
136
+ Exclude:
137
+ - '**/spec/support/**/*'
138
+ - '**/spec/factories/**/*'
139
+ VersionAdded: '0.10.0'
140
+
106
141
  Vicenzo/RSpec/LetIsMethodResult:
107
142
  Description: 'A let under an example group describing a method must not hold the result of calling that method on the subject.'
108
143
  Enabled: true
@@ -194,6 +229,21 @@ Vicenzo/RSpec/TopLevelSubject:
194
229
  - '**/spec/factories/**/*'
195
230
  VersionAdded: '0.8.0'
196
231
 
232
+ Vicenzo/RSpec/UnnestedContextImproperStart:
233
+ Description: 'Check if a context with no parent scenario starts with a conjunction.'
234
+ Enabled: true
235
+ Severity: convention
236
+ ForbiddenPrefixes:
237
+ - and
238
+ - but
239
+ - however
240
+ Include:
241
+ - '**/spec/**/*_spec.rb'
242
+ Exclude:
243
+ - '**/spec/support/**/*'
244
+ - '**/spec/factories/**/*'
245
+ VersionAdded: '0.9.0'
246
+
197
247
  Vicenzo/Style/JsonParseSymbolizeNames:
198
248
  Description: 'Enforces passing `symbolize_names: true` to `JSON.parse` so keys are symbols.'
199
249
  Enabled: true
@@ -205,3 +255,10 @@ Vicenzo/Style/MultilineMethodCallParentheses:
205
255
  Enabled: true
206
256
  AllowedMethods: []
207
257
  VersionAdded: '0.3.0'
258
+
259
+ Vicenzo/Style/PreferManyOverSizeComparison:
260
+ Description: 'Use many? instead of comparing size or length with one.'
261
+ Enabled: true
262
+ Severity: convention
263
+ SafeAutoCorrect: false
264
+ VersionAdded: '0.10.0'
data/config/style.yml ADDED
@@ -0,0 +1,228 @@
1
+ plugins:
2
+ - rubocop-vicenzo
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ SuggestExtensions: false
7
+ NewCops: enable
8
+ Exclude:
9
+ - 'node_modules/**/*'
10
+ - 'vendor/**/*'
11
+ - 'bin/**/*'
12
+ - '.git/**/*'
13
+ - 'db/**/*'
14
+
15
+ ##### LAYOUT #####
16
+
17
+ Layout/ArgumentAlignment:
18
+ Enabled: true
19
+ EnforcedStyle: with_fixed_indentation
20
+
21
+ Layout/CaseIndentation:
22
+ Enabled: true
23
+ EnforcedStyle: end
24
+
25
+ Layout/EndAlignment:
26
+ Enabled: true
27
+ EnforcedStyleAlignWith: variable
28
+
29
+ Layout/FirstArgumentIndentation:
30
+ Enabled: true
31
+ EnforcedStyle: consistent
32
+
33
+ Layout/FirstArrayElementIndentation:
34
+ Enabled: true
35
+ EnforcedStyle: consistent
36
+
37
+ Layout/FirstHashElementIndentation:
38
+ Enabled: true
39
+ EnforcedStyle: consistent
40
+
41
+ Layout/FirstMethodArgumentLineBreak:
42
+ Enabled: true
43
+
44
+ Layout/LineLength:
45
+ Max: 120
46
+ # (\A|\s) -> Start of line or whitespace
47
+ # # -> The hash character
48
+ # (?!\{) -> Negative lookahead: NOT followed by `{` (prevents matching interpolation)
49
+ AllowedPatterns: ['(\A|\s)#(?!\{)']
50
+
51
+ Layout/MultilineMethodArgumentLineBreaks:
52
+ Enabled: true
53
+
54
+ Layout/MultilineMethodCallBraceLayout:
55
+ Enabled: true
56
+ EnforcedStyle: new_line
57
+
58
+ Layout/MultilineMethodCallIndentation:
59
+ Enabled: true
60
+ EnforcedStyle: indented
61
+
62
+ Layout/FirstArrayElementLineBreak:
63
+ Enabled: true
64
+
65
+ Layout/MultilineArrayLineBreaks:
66
+ Enabled: true
67
+
68
+ Layout/MultilineArrayBraceLayout:
69
+ Enabled: true
70
+ EnforcedStyle: new_line
71
+
72
+ Layout/FirstHashElementLineBreak:
73
+ Enabled: true
74
+
75
+ Layout/MultilineHashKeyLineBreaks:
76
+ Enabled: true
77
+
78
+ Layout/MultilineHashBraceLayout:
79
+ Enabled: true
80
+ EnforcedStyle: new_line
81
+
82
+ ##### STYLE #####
83
+
84
+ Style/PreferredHashMethods:
85
+ Enabled: true
86
+ Safe: false
87
+ EnforcedStyle: verbose
88
+
89
+ Style/SymbolArray:
90
+ Enabled: true
91
+ EnforcedStyle: percent
92
+ MinSize: 3
93
+
94
+ Style/WordArray:
95
+ Enabled: true
96
+ EnforcedStyle: percent
97
+ MinSize: 3
98
+
99
+ Style/MultilineBlockChain:
100
+ Enabled: false
101
+
102
+ Style/Lambda:
103
+ Enabled: true
104
+ EnforcedStyle: literal
105
+
106
+ Style/LambdaCall:
107
+ Enabled: false
108
+
109
+ Style/Documentation:
110
+ Enabled: false
111
+
112
+ ##### LINT #####
113
+
114
+ Lint/MissingSuper:
115
+ Enabled: false
116
+
117
+ ##### NAMING #####
118
+
119
+ Naming/InclusiveLanguage:
120
+ Enabled: false
121
+
122
+ Naming/PredicatePrefix:
123
+ ForbiddenPrefixes:
124
+ - is_
125
+ - have_
126
+ # allows `has_` predicate
127
+
128
+ # A single letter says nothing about what was rescued.
129
+ Naming/RescuedExceptionsVariableName:
130
+ Enabled: true
131
+ PreferredName: error
132
+
133
+ # Vicenzo/Naming/ShortName covers local variables. Block parameters are the core
134
+ # cop's job, but it ships with MinNameLength: 1 and therefore never fires — raise
135
+ # it so both halves of the rule agree.
136
+ Naming/BlockParameterName:
137
+ MinNameLength: 3
138
+
139
+ ###### METRICS #####
140
+
141
+ Metrics/AbcSize:
142
+ Enabled: true
143
+ Max: 22
144
+
145
+ Metrics/BlockLength:
146
+ Enabled: true
147
+ CountComments: false
148
+ CountAsOne:
149
+ - array
150
+ - hash
151
+ - heredoc
152
+ - method_call
153
+ AllowedMethods:
154
+ - 'describe'
155
+ - 'context'
156
+ - 'it'
157
+ - 'let'
158
+ - 'subject'
159
+ - 'namespace'
160
+ - 'task'
161
+ - 'configure'
162
+ Exclude:
163
+ - '**/*.gemspec'
164
+ - 'Rakefile'
165
+ - 'rakelib/**/*'
166
+ - 'spec/**/*'
167
+
168
+ Metrics/ClassLength:
169
+ Enabled: true
170
+ Max: 200
171
+ CountAsOne:
172
+ - array
173
+ - hash
174
+ - heredoc
175
+ - method_call
176
+
177
+ Metrics/MethodLength:
178
+ Enabled: true
179
+ CountAsOne:
180
+ - array
181
+ - hash
182
+ - heredoc
183
+ - method_call
184
+ Max: 20
185
+
186
+ Metrics/ModuleLength:
187
+ Enabled: true
188
+ Max: 200
189
+ CountAsOne:
190
+ - array
191
+ - hash
192
+ - heredoc
193
+ - method_call
194
+
195
+ ##### RSPEC #####
196
+
197
+ RSpec/ContextWording:
198
+ Prefixes:
199
+ - and
200
+ - but
201
+ - when
202
+ - with
203
+ - without
204
+
205
+ RSpec/ExampleLength:
206
+ Max: 20
207
+ CountAsOne:
208
+ - array
209
+ - hash
210
+ - heredoc
211
+ - method_call
212
+
213
+ RSpec/MultipleMemoizedHelpers:
214
+ Max: 10
215
+
216
+ RSpec/NestedGroups:
217
+ Enabled: false
218
+
219
+ RSpec/PredicateMatcher:
220
+ Strict: false
221
+
222
+ ##### VICENZO #####
223
+
224
+ Vicenzo/Style/MultilineMethodCallParentheses:
225
+ AllowedMethods:
226
+ - to
227
+ - not_to
228
+ - to_not
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Vicenzo
6
+ module Naming
7
+ # Requires every declaration to carry a name, even when the value it
8
+ # holds is not used.
9
+ #
10
+ # A bare `_` states that a value is being ignored, but erases what that
11
+ # value is. Unused today does not mean unusable tomorrow: when the code
12
+ # grows, nobody reading the signature can tell whether the discarded
13
+ # value is the one the new problem needs.
14
+ #
15
+ # The cop only demands that a name exists — it cannot judge whether a
16
+ # name is a good one. That is the author's call, and the offense message
17
+ # asks for a name taken from the domain (`_previous_owner`) rather than
18
+ # one taken from the shape of the value (`_value`, `_key`, `_item`).
19
+ #
20
+ # Anonymous forwarding parameters (`*`, `**`, `&`, `...`) are ignored:
21
+ # they forward arguments instead of discarding them, and `Style/ArgumentsForwarding`
22
+ # actively pushes code towards that form.
23
+ #
24
+ # @example block parameters
25
+ # # bad
26
+ # catalog.each { |_, copies| copies.each(&:reserve) }
27
+ #
28
+ # # good
29
+ # catalog.each { |_isbn, copies| copies.each(&:reserve) }
30
+ #
31
+ # @example destructured block parameters
32
+ # # bad
33
+ # entries.each { |(_, id)| track(id) }
34
+ #
35
+ # # good
36
+ # entries.each { |(_label, id)| track(id) }
37
+ #
38
+ # @example method parameters
39
+ # # bad
40
+ # def notify(reader, _)
41
+ # reader.deliver
42
+ # end
43
+ #
44
+ # # good
45
+ # def notify(reader, _delivery_channel)
46
+ # reader.deliver
47
+ # end
48
+ #
49
+ # @example multiple assignment
50
+ # # bad
51
+ # title, _ = line.split(';')
52
+ #
53
+ # # good
54
+ # title, _author = line.split(';')
55
+ #
56
+ # @example rescued errors
57
+ # # bad
58
+ # begin
59
+ # shelf.reorder
60
+ # rescue ShelfLocked => _
61
+ # shelf.skip
62
+ # end
63
+ #
64
+ # # good
65
+ # begin
66
+ # shelf.reorder
67
+ # rescue ShelfLocked => _lock_conflict
68
+ # shelf.skip
69
+ # end
70
+ #
71
+ # @example anonymous forwarding (ignored)
72
+ # # good
73
+ # def notify(*)
74
+ # channel.deliver(*)
75
+ # end
76
+ #
77
+ # # good
78
+ # def notify(...)
79
+ # channel.deliver(...)
80
+ # end
81
+ class AnonymousUnusedName < Base
82
+ MSG = 'Name this %<subject>s: an unused value still means something, and `_` hides what. ' \
83
+ 'Name it after what it stands for in the domain (`_previous_owner`), not after its ' \
84
+ 'shape (`_value`, `_key`, `_item`) — that tells the next reader nothing.'
85
+
86
+ BLOCK_PARAMETER = 'block parameter'
87
+ METHOD_PARAMETER = 'method parameter'
88
+ DISCARDED_VALUE = 'discarded value'
89
+ RESCUED_ERROR = 'rescued error'
90
+
91
+ ANONYMOUS_NAME_PATTERN = /\A_+\z/
92
+
93
+ NAMED_ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg shadowarg].freeze
94
+ DEFAULT_VALUE_ARGUMENT_TYPES = %i[optarg kwoptarg].freeze
95
+
96
+ # Numbered (`_1`) and implicit (`it`) block parameters are never
97
+ # declared, so they carry no name to check — both node types answer
98
+ # `arguments` with an empty list.
99
+ def on_block(node)
100
+ check_arguments(node.arguments, BLOCK_PARAMETER)
101
+ end
102
+ alias on_numblock on_block
103
+ alias on_itblock on_block
104
+
105
+ def on_def(node)
106
+ check_arguments(node.arguments, METHOD_PARAMETER)
107
+ end
108
+ alias on_defs on_def
109
+
110
+ def on_masgn(node)
111
+ node.lhs.assignments.each do |assignment|
112
+ next unless assignment.respond_to?(:lvasgn_type?) && assignment.lvasgn_type?
113
+
114
+ register_offense(assignment, DISCARDED_VALUE) if anonymous_name?(assignment.children.first)
115
+ end
116
+ end
117
+
118
+ def on_resbody(node)
119
+ error = node.exception_variable
120
+ return if error.nil?
121
+
122
+ register_offense(error, RESCUED_ERROR) if anonymous_name?(error.children.first)
123
+ end
124
+
125
+ private
126
+
127
+ def check_arguments(arguments, subject)
128
+ arguments.each do |argument|
129
+ if argument.mlhs_type?
130
+ check_arguments(argument.children, subject)
131
+ elsif anonymous_argument?(argument)
132
+ register_offense(argument_range(argument), subject)
133
+ end
134
+ end
135
+ end
136
+
137
+ # An anonymous forwarding parameter (`*`, `**`, `&`) carries no name
138
+ # child at all, and `...` is a `forward_arg`, outside the listed types.
139
+ # Both fall through as allowed.
140
+ def anonymous_argument?(argument)
141
+ NAMED_ARGUMENT_TYPES.include?(argument.type) && anonymous_name?(argument.children.first)
142
+ end
143
+
144
+ # An argument carrying a default value spans the whole expression, so
145
+ # only its name is highlighted; every other form is short enough that
146
+ # showing it whole (`*_`, `**_`, `&_`) tells the reader more.
147
+ def argument_range(argument)
148
+ return argument.loc.name if DEFAULT_VALUE_ARGUMENT_TYPES.include?(argument.type)
149
+
150
+ argument.source_range
151
+ end
152
+
153
+ def anonymous_name?(name)
154
+ !name.nil? && ANONYMOUS_NAME_PATTERN.match?(name.to_s)
155
+ end
156
+
157
+ def register_offense(node, subject)
158
+ add_offense(node, message: format(MSG, subject: subject))
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end