rubocop-prefer_it_parameter 1.0.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 +7 -0
- data/CHANGELOG.md +10 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +136 -0
- data/config/default.yml +6 -0
- data/lib/rubocop/cop/style/prefer_it_parameter.rb +233 -0
- data/lib/rubocop/prefer_it_parameter/plugin.rb +38 -0
- data/lib/rubocop/prefer_it_parameter/version.rb +7 -0
- data/lib/rubocop/prefer_it_parameter.rb +7 -0
- data/lib/rubocop-prefer_it_parameter.rb +3 -0
- data/sig/rubocop/cop/style/prefer_it_parameter.rbs +148 -0
- data/sig/rubocop/prefer_it_parameter/plugin.rbs +16 -0
- data/sig/rubocop/prefer_it_parameter/version.rbs +7 -0
- data/sig/rubocop/prefer_it_parameter.rbs +2 -0
- data/sig/rubocop-prefer_it_parameter.rbs +2 -0
- metadata +88 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5cc86f9725bb1e055df24642820dd33b8f6783b9a8b493f9ee4b8fbb74672107
|
|
4
|
+
data.tar.gz: d6baada0d759d55b85a28966463ebb479d4db86aeaebe4cd09ea5bfeca4b9310
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d5828d52ff9ecf241a7c27f8b623ce4b7c479492c96c0600c992bb39eb5620a9b65c2a6914d1bf4aeaae0451295a4961a1105bc5af8c1f05f2a731b51e30970b
|
|
7
|
+
data.tar.gz: afd728b9c7367f1e4af45a695169fa3fd07fb1b52ca6ab855c56af99e09bacc312f1074458ba580befa36a8b769e5a170badf166ba0763ddd41538974825cf40
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [1.0.0] - 2026-08-05
|
|
4
|
+
|
|
5
|
+
Initial release.
|
|
6
|
+
|
|
7
|
+
- `Style/PreferItParameter`: recommends the `it` block parameter (Ruby 3.4+) over a named
|
|
8
|
+
block argument in single-line blocks. Autocorrection is available but unsafe, so
|
|
9
|
+
`rubocop -A` is required to apply it. Only projects with `TargetRubyVersion` 3.4 or
|
|
10
|
+
higher are inspected; see the README for the cases the cop skips.
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
"rubocop-prefer_it_parameter" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
|
|
4
|
+
|
|
5
|
+
* Participants will be tolerant of opposing views.
|
|
6
|
+
* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
|
7
|
+
* When interpreting the words and actions of others, participants should always assume good intentions.
|
|
8
|
+
* Behaviour which can be reasonably considered harassment will not be tolerated.
|
|
9
|
+
|
|
10
|
+
If you have any concerns about behaviour within this project, please contact us at ["i.tkomiya@gmail.com"](mailto:"i.tkomiya@gmail.com").
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Takeshi KOMIYA
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# rubocop-prefer_it_parameter
|
|
2
|
+
|
|
3
|
+
A RuboCop plugin that recommends using the `it` block parameter (Ruby 3.4+) instead of named block arguments in single-line blocks.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add the gem to your application's Gemfile. RuboCop loads it through `plugins:`, so it does not need to be required:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
group :development do
|
|
11
|
+
gem "rubocop-prefer_it_parameter", require: false
|
|
12
|
+
end
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or install it directly:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
gem install rubocop-prefer_it_parameter
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then add it to your `.rubocop.yml`:
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
plugins:
|
|
25
|
+
- rubocop-prefer_it_parameter
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Cops
|
|
29
|
+
|
|
30
|
+
### Style/PreferItParameter
|
|
31
|
+
|
|
32
|
+
Prefer the `it` block parameter over a named block argument in single-line blocks. Only blocks consisting of a single statement are converted. This cop supports autocorrection.
|
|
33
|
+
|
|
34
|
+
**Bad:**
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
users.map { |user| user.name.upcase }
|
|
38
|
+
items.select { |item| item.active? && item.visible? }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Good:**
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
users.map { it.name.upcase }
|
|
45
|
+
items.select { it.active? && it.visible? }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A value omission is spelled out, since `{it:}` would call a method named `it`:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# bad
|
|
52
|
+
items.map { |item| {item:} }
|
|
53
|
+
|
|
54
|
+
# good
|
|
55
|
+
items.map { {item: it} }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The autocorrection is marked unsafe — see [Safety](#safety).
|
|
59
|
+
|
|
60
|
+
#### Exceptions
|
|
61
|
+
|
|
62
|
+
A block is left alone when it:
|
|
63
|
+
|
|
64
|
+
- is multi-line — a named argument reads better there
|
|
65
|
+
- has two or more statements
|
|
66
|
+
- contains a nested block — only the innermost block is converted
|
|
67
|
+
- takes anything other than a single plain argument, including a trailing comma such as `|x,|` (which destructures the yielded value while `it` does not)
|
|
68
|
+
- rebinds its argument, by assignment or by pattern matching
|
|
69
|
+
- never references its argument (see `Lint/UnusedBlockArgument`)
|
|
70
|
+
- references a local variable named `it` from an enclosing scope, or assigns to `it`
|
|
71
|
+
- already names its argument `it` — dropping it would revive an `it` from an enclosing scope (`Style/ItAssignment` forbids the name instead)
|
|
72
|
+
- defines a callable or a method — `->(x) { }`, `lambda`, `proc`, `Proc.new`, `define_method`, `define_singleton_method` — since the parameter list is part of its API and `it` drops the parameter name
|
|
73
|
+
|
|
74
|
+
## Related cops
|
|
75
|
+
|
|
76
|
+
### `Style/ItBlockParameter` (RuboCop core)
|
|
77
|
+
|
|
78
|
+
It ships as `Enabled: pending`, so it needs `NewCops: enable` or an explicit `Enabled: true`. Once enabled, the two cops do not conflict — they cover different cells of the same grid:
|
|
79
|
+
|
|
80
|
+
| | `Style/PreferItParameter` (this gem) | `Style/ItBlockParameter` (core, `allow_single_line`) |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| single-line block with a named argument | offense | — |
|
|
83
|
+
| single-line block using `_1` | — | offense |
|
|
84
|
+
| multi-line block using `_1` | — | offense |
|
|
85
|
+
| multi-line block using `it` | — | offense |
|
|
86
|
+
| multi-line block with a named argument | — | — |
|
|
87
|
+
|
|
88
|
+
Enabling both enforces "use `it` for single-line blocks, use a named argument for multi-line blocks" consistently.
|
|
89
|
+
|
|
90
|
+
Do not set core's cop to `EnforcedStyle: always` — it then checks named block arguments as well, and the two autocorrections collide on the same block.
|
|
91
|
+
|
|
92
|
+
### `Style/ItAssignment` (RuboCop core) — recommended
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
Style/ItAssignment:
|
|
96
|
+
Enabled: true
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`Style/ItAssignment` forbids naming a local variable or parameter `it`, which `Style/PreferItParameter` cannot fully guard against on its own — see [Safety](#safety).
|
|
100
|
+
|
|
101
|
+
## Safety
|
|
102
|
+
|
|
103
|
+
The autocorrection is marked unsafe (`SafeAutoCorrect: false`), so `rubocop -a` reports the offenses without changing anything and `rubocop -A` is needed to apply them. `it` is not equivalent to a named argument in every respect:
|
|
104
|
+
|
|
105
|
+
- A local variable or parameter named `it` takes precedence over the block parameter. The cop skips a block that references such a variable, but it cannot detect one that the block never references — there the autocorrection silently changes what the block sees. Enabling `Style/ItAssignment` is therefore a prerequisite.
|
|
106
|
+
- `Proc#parameters` loses the argument name: `[[:opt, :x]]` becomes `[[:opt]]`. Blocks that define a callable or a method are excluded for this reason, but a block captured with `&block` and introspected elsewhere is still affected.
|
|
107
|
+
- `binding.local_variable_get(:x)` inside the block stops working.
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
- Ruby >= 3.4
|
|
112
|
+
- RuboCop >= 1.75.0
|
|
113
|
+
|
|
114
|
+
The cop only inspects projects whose `TargetRubyVersion` is 3.4 or higher, since that is when `it` was introduced.
|
|
115
|
+
|
|
116
|
+
## Development
|
|
117
|
+
|
|
118
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run the whole check suite (RuboCop, RSpec, Steep and RBS validation), or `bundle exec rake spec` for the tests alone. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
119
|
+
|
|
120
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
121
|
+
|
|
122
|
+
## Releasing
|
|
123
|
+
|
|
124
|
+
Bump `VERSION` in `lib/rubocop/prefer_it_parameter/version.rb`, add an entry to [CHANGELOG.md](CHANGELOG.md), and merge that into `main`. The [release workflow](.github/workflows/release.yml) picks up the change to `version.rb`, tags the version, and pushes the gem to [rubygems.org](https://rubygems.org) through trusted publishing. It can also be started by hand from the Actions tab.
|
|
125
|
+
|
|
126
|
+
## Contributing
|
|
127
|
+
|
|
128
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/tk0miya/rubocop-prefer_it_parameter. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/tk0miya/rubocop-prefer_it_parameter/blob/main/CODE_OF_CONDUCT.md).
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
133
|
+
|
|
134
|
+
## Code of Conduct
|
|
135
|
+
|
|
136
|
+
Everyone interacting in the rubocop-prefer_it_parameter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/tk0miya/rubocop-prefer_it_parameter/blob/main/CODE_OF_CONDUCT.md).
|
data/config/default.yml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Style/PreferItParameter:
|
|
2
|
+
Description: 'Prefer the `it` block parameter over a named block argument in single-line blocks.'
|
|
3
|
+
Reference: https://github.com/tk0miya/rubocop-prefer_it_parameter#stylepreferitparameter
|
|
4
|
+
Enabled: true
|
|
5
|
+
SafeAutoCorrect: false
|
|
6
|
+
VersionAdded: '1.0'
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Prefer the `it` block parameter over a named block argument in single-line blocks.
|
|
7
|
+
#
|
|
8
|
+
# Only blocks consisting of a single statement are converted. This cop complements
|
|
9
|
+
# `Style/ItBlockParameter` in RuboCop core, which converts `_1` to `it` but in its
|
|
10
|
+
# default style does not check named block arguments.
|
|
11
|
+
#
|
|
12
|
+
# @safety
|
|
13
|
+
# The autocorrection is unsafe because a local variable named `it` that the
|
|
14
|
+
# block does not reference cannot be detected, and replacing it would silently
|
|
15
|
+
# change what the block sees. Enabling `Style/ItAssignment` rules such names
|
|
16
|
+
# out. Additionally, `it` drops the argument name from `Proc#parameters`
|
|
17
|
+
# (`[[:opt, :x]]` becomes `[[:opt]]`) and breaks
|
|
18
|
+
# `binding.local_variable_get(:x)` inside the block. Blocks that define a
|
|
19
|
+
# callable or a method are skipped for that reason, but a block captured with
|
|
20
|
+
# `&block` and introspected elsewhere is still affected.
|
|
21
|
+
#
|
|
22
|
+
# @example
|
|
23
|
+
# # bad
|
|
24
|
+
# users.map { |user| user.name.upcase }
|
|
25
|
+
# items.select { |item| item.active? && item.visible? }
|
|
26
|
+
#
|
|
27
|
+
# # good
|
|
28
|
+
# users.map { it.name.upcase }
|
|
29
|
+
# items.select { it.active? && it.visible? }
|
|
30
|
+
#
|
|
31
|
+
# # good - multi-line block
|
|
32
|
+
# users.each do |user|
|
|
33
|
+
# user.activate!
|
|
34
|
+
# notify(user)
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# # good - the block consists of two statements
|
|
38
|
+
# items.each { |item| validate(item); save(item) }
|
|
39
|
+
#
|
|
40
|
+
# # good - only the innermost block may use `it`
|
|
41
|
+
# matrix.map { |row| row.map { it * 2 } }
|
|
42
|
+
#
|
|
43
|
+
# # good - `|x,|` destructures the yielded value, unlike `it`
|
|
44
|
+
# pairs.each { |pair,| puts pair }
|
|
45
|
+
#
|
|
46
|
+
# # good - a callable's parameter list is part of its API
|
|
47
|
+
# ->(x) { puts x }
|
|
48
|
+
# define_method(:m) { |x| x + 1 }
|
|
49
|
+
#
|
|
50
|
+
# # bad
|
|
51
|
+
# items.map { |item| {item:} }
|
|
52
|
+
#
|
|
53
|
+
# # good - the value is spelled out, since `{it:}` would call a method named `it`
|
|
54
|
+
# items.map { {item: it} }
|
|
55
|
+
#
|
|
56
|
+
class PreferItParameter < Base
|
|
57
|
+
extend AutoCorrector
|
|
58
|
+
extend TargetRubyVersion
|
|
59
|
+
include RangeHelp
|
|
60
|
+
|
|
61
|
+
minimum_target_ruby_version 3.4
|
|
62
|
+
|
|
63
|
+
MSG = "Use the `it` block parameter instead of the named block argument `%<name>s`."
|
|
64
|
+
|
|
65
|
+
INNER_BLOCK_TYPES = %i[block numblock itblock].freeze #: Array[Symbol]
|
|
66
|
+
|
|
67
|
+
CALLABLE_METHODS = %i[define_method define_singleton_method lambda proc].freeze #: Array[Symbol]
|
|
68
|
+
|
|
69
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
70
|
+
def on_block(node) #: void
|
|
71
|
+
body = node.body
|
|
72
|
+
return unless body
|
|
73
|
+
|
|
74
|
+
name = convertible_argument_name(node, body)
|
|
75
|
+
return unless name
|
|
76
|
+
|
|
77
|
+
references = lvar_references(body, name)
|
|
78
|
+
return if references.empty?
|
|
79
|
+
|
|
80
|
+
register_offense(node, name, references)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
86
|
+
# @rbs body: RuboCop::AST::Node
|
|
87
|
+
def convertible_argument_name(node, body) #: Symbol?
|
|
88
|
+
return unless node.single_line?
|
|
89
|
+
return if defines_callable?(node)
|
|
90
|
+
|
|
91
|
+
name = sole_argument_name(node)
|
|
92
|
+
return unless name
|
|
93
|
+
return unless convertible_body?(body, name)
|
|
94
|
+
return if shadows_it?(body)
|
|
95
|
+
|
|
96
|
+
name
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# `it` would not mean what the block expects when a local variable named `it`
|
|
100
|
+
# is already in scope, or when the body assigns to `it` — assigning turns `it`
|
|
101
|
+
# into a plain local variable and disables the implicit block parameter.
|
|
102
|
+
#
|
|
103
|
+
# @rbs body: RuboCop::AST::Node
|
|
104
|
+
def shadows_it?(body) #: bool
|
|
105
|
+
lvar_references(body, :it).any? || reassigned?(body, :it)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# `it` drops the parameter name from `Proc#parameters`, which changes the
|
|
109
|
+
# meaning of a block that defines a callable object or a method: there the
|
|
110
|
+
# parameter list is part of the API, unlike a block passed to `each` or `map`.
|
|
111
|
+
#
|
|
112
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
113
|
+
def defines_callable?(node) #: bool
|
|
114
|
+
CALLABLE_METHODS.include?(node.method_name) || proc_new?(node)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
118
|
+
def proc_new?(node) #: bool
|
|
119
|
+
return false unless node.method?(:new)
|
|
120
|
+
|
|
121
|
+
receiver = node.send_node.receiver
|
|
122
|
+
return false unless receiver&.const_type?
|
|
123
|
+
|
|
124
|
+
const = receiver #: RuboCop::AST::ConstNode
|
|
125
|
+
const.short_name == :Proc
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
129
|
+
def sole_argument_name(node) #: Symbol?
|
|
130
|
+
return unless node.argument_list.one?
|
|
131
|
+
|
|
132
|
+
argument = node.first_argument
|
|
133
|
+
# Rules out optarg, restarg, kwarg, blockarg, shadowarg and mlhs at once.
|
|
134
|
+
return unless argument&.arg_type?
|
|
135
|
+
# `|x,|` is indistinguishable from `|x|` in the AST even though it
|
|
136
|
+
# destructures the yielded value, so the source has to be checked.
|
|
137
|
+
return if node.arguments.source&.include?(",")
|
|
138
|
+
|
|
139
|
+
plain_argument = argument #: RuboCop::AST::ArgNode
|
|
140
|
+
plain_argument.name
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @rbs body: RuboCop::AST::Node
|
|
144
|
+
# @rbs name: Symbol
|
|
145
|
+
def convertible_body?(body, name) #: bool
|
|
146
|
+
single_statement?(body) && !contains_block?(body) && !reassigned?(body, name)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# `begin` (parentheses) and `kwbegin` (`begin ... end`) both wrap a sequence of
|
|
150
|
+
# statements as well as a single expression, so the number of children is what
|
|
151
|
+
# tells the two apart.
|
|
152
|
+
#
|
|
153
|
+
# @rbs body: RuboCop::AST::Node
|
|
154
|
+
def single_statement?(body) #: bool
|
|
155
|
+
return body.each_child_node.one? if body.type?(:begin, :kwbegin)
|
|
156
|
+
|
|
157
|
+
true
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# `it` is a syntax error inside a block that has an ordinary parameter, and
|
|
161
|
+
# silently shadows the outer one inside a parameterless block, so only the
|
|
162
|
+
# innermost block is converted.
|
|
163
|
+
#
|
|
164
|
+
# @rbs body: RuboCop::AST::Node
|
|
165
|
+
def contains_block?(body) #: bool
|
|
166
|
+
body.each_node(*INNER_BLOCK_TYPES).any?
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# A block that rebinds the name cannot be converted: the value `it` refers to
|
|
170
|
+
# would no longer be the one the block was yielded. `match_var` covers pattern
|
|
171
|
+
# matching (`1 in x`), which rebinds just like an assignment.
|
|
172
|
+
#
|
|
173
|
+
# @rbs body: RuboCop::AST::Node
|
|
174
|
+
# @rbs name: Symbol
|
|
175
|
+
def reassigned?(body, name) #: bool
|
|
176
|
+
body.each_node(:lvasgn, :match_var).any? do |node|
|
|
177
|
+
node.to_a.first == name
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Returns the enclosing pair when the reference is a value omission (`{x:}`,
|
|
182
|
+
# `foo(x:)`). Such a reference shares its source range with the label, so the
|
|
183
|
+
# value has to be written after the pair rather than replaced.
|
|
184
|
+
#
|
|
185
|
+
# @rbs reference: RuboCop::AST::Node
|
|
186
|
+
def omitted_value_pair(reference) #: RuboCop::AST::PairNode?
|
|
187
|
+
parent = reference.parent
|
|
188
|
+
return unless parent&.pair_type?
|
|
189
|
+
|
|
190
|
+
pair = parent #: RuboCop::AST::PairNode
|
|
191
|
+
pair if pair.value_omission?
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# @rbs body: RuboCop::AST::Node
|
|
195
|
+
# @rbs name: Symbol
|
|
196
|
+
def lvar_references(body, name) #: Array[RuboCop::AST::Node]
|
|
197
|
+
body.each_node(:lvar).select do |node|
|
|
198
|
+
variable = node #: RuboCop::AST::VarNode
|
|
199
|
+
variable.name == name
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
204
|
+
# @rbs name: Symbol
|
|
205
|
+
# @rbs references: Array[RuboCop::AST::Node]
|
|
206
|
+
def register_offense(node, name, references) #: void
|
|
207
|
+
add_offense(node.arguments, message: format(MSG, name:)) do |corrector|
|
|
208
|
+
references.each do |reference|
|
|
209
|
+
replace_reference(corrector, reference)
|
|
210
|
+
end
|
|
211
|
+
corrector.remove(arguments_removal_range(node))
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# @rbs corrector: RuboCop::Cop::Corrector
|
|
216
|
+
# @rbs reference: RuboCop::AST::Node
|
|
217
|
+
def replace_reference(corrector, reference) #: void
|
|
218
|
+
pair = omitted_value_pair(reference)
|
|
219
|
+
if pair
|
|
220
|
+
corrector.insert_after(pair.source_range, " it")
|
|
221
|
+
else
|
|
222
|
+
corrector.replace(reference.source_range, "it")
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
227
|
+
def arguments_removal_range(node) #: Parser::Source::Range
|
|
228
|
+
range_with_surrounding_space(node.arguments.source_range, side: :right, newlines: false)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lint_roller"
|
|
4
|
+
require "pathname"
|
|
5
|
+
|
|
6
|
+
require_relative "version"
|
|
7
|
+
|
|
8
|
+
module RuboCop
|
|
9
|
+
module PreferItParameter
|
|
10
|
+
class Plugin < LintRoller::Plugin
|
|
11
|
+
# @rbs override
|
|
12
|
+
def about
|
|
13
|
+
LintRoller::About.new(
|
|
14
|
+
name: "rubocop-prefer_it_parameter",
|
|
15
|
+
version: VERSION,
|
|
16
|
+
homepage: "https://github.com/tk0miya/rubocop-prefer_it_parameter",
|
|
17
|
+
description: "A RuboCop plugin that recommends using the `it` block parameter " \
|
|
18
|
+
"instead of named block arguments in single-line blocks."
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @rbs override
|
|
23
|
+
def supported?(context)
|
|
24
|
+
context.engine == :rubocop
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @rbs override
|
|
28
|
+
def rules(_context)
|
|
29
|
+
LintRoller::Rules.new(
|
|
30
|
+
type: :path,
|
|
31
|
+
config_format: :rubocop,
|
|
32
|
+
# `__dir__` is `String?` in the RBS stubs, so `__FILE__` is used instead.
|
|
33
|
+
value: Pathname.new(__FILE__).dirname.join("../../../config/default.yml").expand_path
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Generated from lib/rubocop/cop/style/prefer_it_parameter.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Prefer the `it` block parameter over a named block argument in single-line blocks.
|
|
7
|
+
#
|
|
8
|
+
# Only blocks consisting of a single statement are converted. This cop complements
|
|
9
|
+
# `Style/ItBlockParameter` in RuboCop core, which converts `_1` to `it` but in its
|
|
10
|
+
# default style does not check named block arguments.
|
|
11
|
+
#
|
|
12
|
+
# @safety
|
|
13
|
+
# The autocorrection is unsafe because a local variable named `it` that the
|
|
14
|
+
# block does not reference cannot be detected, and replacing it would silently
|
|
15
|
+
# change what the block sees. Enabling `Style/ItAssignment` rules such names
|
|
16
|
+
# out. Additionally, `it` drops the argument name from `Proc#parameters`
|
|
17
|
+
# (`[[:opt, :x]]` becomes `[[:opt]]`) and breaks
|
|
18
|
+
# `binding.local_variable_get(:x)` inside the block. Blocks that define a
|
|
19
|
+
# callable or a method are skipped for that reason, but a block captured with
|
|
20
|
+
# `&block` and introspected elsewhere is still affected.
|
|
21
|
+
#
|
|
22
|
+
# @example
|
|
23
|
+
# # bad
|
|
24
|
+
# users.map { |user| user.name.upcase }
|
|
25
|
+
# items.select { |item| item.active? && item.visible? }
|
|
26
|
+
#
|
|
27
|
+
# # good
|
|
28
|
+
# users.map { it.name.upcase }
|
|
29
|
+
# items.select { it.active? && it.visible? }
|
|
30
|
+
#
|
|
31
|
+
# # good - multi-line block
|
|
32
|
+
# users.each do |user|
|
|
33
|
+
# user.activate!
|
|
34
|
+
# notify(user)
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# # good - the block consists of two statements
|
|
38
|
+
# items.each { |item| validate(item); save(item) }
|
|
39
|
+
#
|
|
40
|
+
# # good - only the innermost block may use `it`
|
|
41
|
+
# matrix.map { |row| row.map { it * 2 } }
|
|
42
|
+
#
|
|
43
|
+
# # good - `|x,|` destructures the yielded value, unlike `it`
|
|
44
|
+
# pairs.each { |pair,| puts pair }
|
|
45
|
+
#
|
|
46
|
+
# # good - a callable's parameter list is part of its API
|
|
47
|
+
# ->(x) { puts x }
|
|
48
|
+
# define_method(:m) { |x| x + 1 }
|
|
49
|
+
#
|
|
50
|
+
# # bad
|
|
51
|
+
# items.map { |item| {item:} }
|
|
52
|
+
#
|
|
53
|
+
# # good - the value is spelled out, since `{it:}` would call a method named `it`
|
|
54
|
+
# items.map { {item: it} }
|
|
55
|
+
class PreferItParameter < Base
|
|
56
|
+
extend AutoCorrector
|
|
57
|
+
|
|
58
|
+
extend TargetRubyVersion
|
|
59
|
+
|
|
60
|
+
include RangeHelp
|
|
61
|
+
|
|
62
|
+
MSG: ::String
|
|
63
|
+
|
|
64
|
+
INNER_BLOCK_TYPES: Array[Symbol]
|
|
65
|
+
|
|
66
|
+
CALLABLE_METHODS: Array[Symbol]
|
|
67
|
+
|
|
68
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
69
|
+
def on_block: (RuboCop::AST::BlockNode node) -> void
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
74
|
+
# @rbs body: RuboCop::AST::Node
|
|
75
|
+
def convertible_argument_name: (RuboCop::AST::BlockNode node, RuboCop::AST::Node body) -> Symbol?
|
|
76
|
+
|
|
77
|
+
# `it` would not mean what the block expects when a local variable named `it`
|
|
78
|
+
# is already in scope, or when the body assigns to `it` — assigning turns `it`
|
|
79
|
+
# into a plain local variable and disables the implicit block parameter.
|
|
80
|
+
#
|
|
81
|
+
# @rbs body: RuboCop::AST::Node
|
|
82
|
+
def shadows_it?: (RuboCop::AST::Node body) -> bool
|
|
83
|
+
|
|
84
|
+
# `it` drops the parameter name from `Proc#parameters`, which changes the
|
|
85
|
+
# meaning of a block that defines a callable object or a method: there the
|
|
86
|
+
# parameter list is part of the API, unlike a block passed to `each` or `map`.
|
|
87
|
+
#
|
|
88
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
89
|
+
def defines_callable?: (RuboCop::AST::BlockNode node) -> bool
|
|
90
|
+
|
|
91
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
92
|
+
def proc_new?: (RuboCop::AST::BlockNode node) -> bool
|
|
93
|
+
|
|
94
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
95
|
+
def sole_argument_name: (RuboCop::AST::BlockNode node) -> Symbol?
|
|
96
|
+
|
|
97
|
+
# @rbs body: RuboCop::AST::Node
|
|
98
|
+
# @rbs name: Symbol
|
|
99
|
+
def convertible_body?: (RuboCop::AST::Node body, Symbol name) -> bool
|
|
100
|
+
|
|
101
|
+
# `begin` (parentheses) and `kwbegin` (`begin ... end`) both wrap a sequence of
|
|
102
|
+
# statements as well as a single expression, so the number of children is what
|
|
103
|
+
# tells the two apart.
|
|
104
|
+
#
|
|
105
|
+
# @rbs body: RuboCop::AST::Node
|
|
106
|
+
def single_statement?: (RuboCop::AST::Node body) -> bool
|
|
107
|
+
|
|
108
|
+
# `it` is a syntax error inside a block that has an ordinary parameter, and
|
|
109
|
+
# silently shadows the outer one inside a parameterless block, so only the
|
|
110
|
+
# innermost block is converted.
|
|
111
|
+
#
|
|
112
|
+
# @rbs body: RuboCop::AST::Node
|
|
113
|
+
def contains_block?: (RuboCop::AST::Node body) -> bool
|
|
114
|
+
|
|
115
|
+
# A block that rebinds the name cannot be converted: the value `it` refers to
|
|
116
|
+
# would no longer be the one the block was yielded. `match_var` covers pattern
|
|
117
|
+
# matching (`1 in x`), which rebinds just like an assignment.
|
|
118
|
+
#
|
|
119
|
+
# @rbs body: RuboCop::AST::Node
|
|
120
|
+
# @rbs name: Symbol
|
|
121
|
+
def reassigned?: (RuboCop::AST::Node body, Symbol name) -> bool
|
|
122
|
+
|
|
123
|
+
# Returns the enclosing pair when the reference is a value omission (`{x:}`,
|
|
124
|
+
# `foo(x:)`). Such a reference shares its source range with the label, so the
|
|
125
|
+
# value has to be written after the pair rather than replaced.
|
|
126
|
+
#
|
|
127
|
+
# @rbs reference: RuboCop::AST::Node
|
|
128
|
+
def omitted_value_pair: (RuboCop::AST::Node reference) -> RuboCop::AST::PairNode?
|
|
129
|
+
|
|
130
|
+
# @rbs body: RuboCop::AST::Node
|
|
131
|
+
# @rbs name: Symbol
|
|
132
|
+
def lvar_references: (RuboCop::AST::Node body, Symbol name) -> Array[RuboCop::AST::Node]
|
|
133
|
+
|
|
134
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
135
|
+
# @rbs name: Symbol
|
|
136
|
+
# @rbs references: Array[RuboCop::AST::Node]
|
|
137
|
+
def register_offense: (RuboCop::AST::BlockNode node, Symbol name, Array[RuboCop::AST::Node] references) -> void
|
|
138
|
+
|
|
139
|
+
# @rbs corrector: RuboCop::Cop::Corrector
|
|
140
|
+
# @rbs reference: RuboCop::AST::Node
|
|
141
|
+
def replace_reference: (RuboCop::Cop::Corrector corrector, RuboCop::AST::Node reference) -> void
|
|
142
|
+
|
|
143
|
+
# @rbs node: RuboCop::AST::BlockNode
|
|
144
|
+
def arguments_removal_range: (RuboCop::AST::BlockNode node) -> Parser::Source::Range
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Generated from lib/rubocop/prefer_it_parameter/plugin.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module PreferItParameter
|
|
5
|
+
class Plugin < LintRoller::Plugin
|
|
6
|
+
# @rbs override
|
|
7
|
+
def about: ...
|
|
8
|
+
|
|
9
|
+
# @rbs override
|
|
10
|
+
def supported?: ...
|
|
11
|
+
|
|
12
|
+
# @rbs override
|
|
13
|
+
def rules: ...
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rubocop-prefer_it_parameter
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Takeshi KOMIYA
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: lint_roller
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rubocop
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.75.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 1.75.0
|
|
40
|
+
description: A RuboCop plugin that recommends using the `it` block parameter instead
|
|
41
|
+
of named block arguments in single-line blocks.
|
|
42
|
+
email:
|
|
43
|
+
- i.tkomiya@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- CHANGELOG.md
|
|
49
|
+
- CODE_OF_CONDUCT.md
|
|
50
|
+
- LICENSE.txt
|
|
51
|
+
- README.md
|
|
52
|
+
- config/default.yml
|
|
53
|
+
- lib/rubocop-prefer_it_parameter.rb
|
|
54
|
+
- lib/rubocop/cop/style/prefer_it_parameter.rb
|
|
55
|
+
- lib/rubocop/prefer_it_parameter.rb
|
|
56
|
+
- lib/rubocop/prefer_it_parameter/plugin.rb
|
|
57
|
+
- lib/rubocop/prefer_it_parameter/version.rb
|
|
58
|
+
- sig/rubocop-prefer_it_parameter.rbs
|
|
59
|
+
- sig/rubocop/cop/style/prefer_it_parameter.rbs
|
|
60
|
+
- sig/rubocop/prefer_it_parameter.rbs
|
|
61
|
+
- sig/rubocop/prefer_it_parameter/plugin.rbs
|
|
62
|
+
- sig/rubocop/prefer_it_parameter/version.rbs
|
|
63
|
+
homepage: https://github.com/tk0miya/rubocop-prefer_it_parameter
|
|
64
|
+
licenses:
|
|
65
|
+
- MIT
|
|
66
|
+
metadata:
|
|
67
|
+
default_lint_roller_plugin: RuboCop::PreferItParameter::Plugin
|
|
68
|
+
source_code_uri: https://github.com/tk0miya/rubocop-prefer_it_parameter
|
|
69
|
+
changelog_uri: https://github.com/tk0miya/rubocop-prefer_it_parameter/blob/main/CHANGELOG.md
|
|
70
|
+
rubygems_mfa_required: 'true'
|
|
71
|
+
rdoc_options: []
|
|
72
|
+
require_paths:
|
|
73
|
+
- lib
|
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '3.4'
|
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
requirements: []
|
|
85
|
+
rubygems_version: 4.0.16
|
|
86
|
+
specification_version: 4
|
|
87
|
+
summary: A RuboCop plugin that recommends using the `it` block parameter.
|
|
88
|
+
test_files: []
|