officer_neetzsche 0.2.0 → 0.4.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/CHANGELOG.md +17 -1
- data/README.md +119 -0
- data/config/default.yml +19 -0
- data/lib/officer_neetzsche/version.rb +1 -1
- data/lib/rubocop/cop/neetzsche/multiline_conditional_body.rb +46 -0
- data/lib/rubocop/cop/neetzsche/statement_modifier.rb +196 -0
- data/lib/rubocop/cop/neetzsche_cops.rb +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 22890bd97c3719ada7fdb61dd55402c68b110045cdfc75e99961e85efbe6d2b6
|
|
4
|
+
data.tar.gz: 61c8a23f85804cfda9b60f5f7b86e79c5afb872d1b42ef72125ff9a364683dcb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 64d16df6a4336e4b0c39f7db504659e565831f581cc3dcbd135a17a034717e5ed75ff3be2c6da1ee2110c3c373c453fae707e393ab5e59b135fac9beaab759b7
|
|
7
|
+
data.tar.gz: 6dc9fcedae06886c4c967d5d74ff7091dbf12264f073b326dfb0c64932c42f0da05f5544051d7ef32b12d88005f6e2cb84e28166613a41172d209dfc2589afa0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
3
|
+
## 0.4.0 (unreleased)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `NEETzsche/MultilineConditionalBody`: flags an `if`, `unless`, `elsif`, `else`, `while`, or `until` whose body is more than one statement, or one statement that spans lines, and asks for the body to be a method of its own. With `NEETzsche/StatementModifier`, that leaves a conditional two shapes: a modifier, or a block whose branches are one statement each. No autocorrect, since the method needs a name.
|
|
8
|
+
|
|
9
|
+
## 0.3.0 (2026-09-15)
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `NEETzsche/StatementModifier`: rewrites an `if`, `unless`, `while`, `until`, or `rescue` whose body is one statement on one line as a statement modifier, whatever the resulting line length. Method-level and block-level `rescue` clauses count. Bodies that are themselves conditionals or rescues, comments inside the block, heredocs, and conditions or bodies whose meaning would change when the body is parsed first are left alone. Autocorrects, adding parentheses where the value is used.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **Breaking:** the plugin now disables `Style/IfUnlessModifier` and `Style/WhileUntilModifier`, which `NEETzsche/StatementModifier` subsumes, and `Style/RescueModifier`, which forbids what it demands. Re-enable them in your own `.rubocop.yml` if you want them back; `Style/IfUnlessModifier` will then fight over modifier lines that exceed `Layout/LineLength`.
|
|
18
|
+
|
|
19
|
+
## 0.2.0 (2026-09-09)
|
|
4
20
|
|
|
5
21
|
### Added
|
|
6
22
|
|
data/README.md
CHANGED
|
@@ -9,7 +9,9 @@ RuboCop cops that enforce NEETzsche-isms. Every cop ships enabled: register the
|
|
|
9
9
|
| Cop | Enforces | Autocorrect |
|
|
10
10
|
| --- | --- | --- |
|
|
11
11
|
| `NEETzsche/GeneratedMigrationTimestamp` | Migration timestamps come from `bin/rails generate migration`, not a keyboard. | No |
|
|
12
|
+
| `NEETzsche/MultilineConditionalBody` | An `if`, `unless`, `while`, or `until` body of more than one statement, or more than one line, is a method of its own. | No |
|
|
12
13
|
| `NEETzsche/NoComments` | No comments. The code explains itself. | Yes |
|
|
14
|
+
| `NEETzsche/StatementModifier` | An `if`, `unless`, `while`, `until`, or `rescue` around one statement is a modifier. | Yes |
|
|
13
15
|
|
|
14
16
|
## Requirements
|
|
15
17
|
|
|
@@ -107,6 +109,67 @@ Projects that set `config.active_record.timestamped_migrations = false` number t
|
|
|
107
109
|
|
|
108
110
|
There is no autocorrect. The fix is to delete the file and run `bin/rails generate migration` again.
|
|
109
111
|
|
|
112
|
+
## NEETzsche/MultilineConditionalBody
|
|
113
|
+
|
|
114
|
+
Flags an `if`, `unless`, `elsif`, `else`, `while`, or `until` whose body is more than one statement, or one statement that spans lines. The fix is to name what the body does and move it into a method of its own, after which `NEETzsche/StatementModifier` rewrites the call site as a modifier. Between them, the two cops leave a conditional two shapes: a modifier, or a block whose branches are one statement each.
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
# bad
|
|
118
|
+
if condition?
|
|
119
|
+
do_this
|
|
120
|
+
do_that
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
while queue.any?
|
|
124
|
+
item = queue.pop
|
|
125
|
+
item.call
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
if condition?
|
|
129
|
+
items.each do |item|
|
|
130
|
+
item.call
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# good
|
|
135
|
+
do_stuff if condition?
|
|
136
|
+
work_one_item while queue.any?
|
|
137
|
+
call_items if condition?
|
|
138
|
+
|
|
139
|
+
def do_stuff
|
|
140
|
+
do_this
|
|
141
|
+
do_that
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def work_one_item
|
|
145
|
+
item = queue.pop
|
|
146
|
+
item.call
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def call_items
|
|
150
|
+
items.each { |item| item.call }
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# good, because each branch is one statement
|
|
154
|
+
if condition?
|
|
155
|
+
do_this
|
|
156
|
+
else
|
|
157
|
+
do_that
|
|
158
|
+
end
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The offense sits on the keyword of the branch: `if`, `unless`, `elsif`, `else`, `while`, or `until`. Each branch is judged on its own, so an `if` with a two-statement body and a one-statement `else` gets one offense, on the `if`. A heredoc counts as the lines it spans. A modifier whose body spans lines, such as `begin ... end if condition?`, is flagged too. A conditional nested inside another is flagged at every level whose body spans lines, and fixing the innermost usually clears the rest.
|
|
162
|
+
|
|
163
|
+
Left alone:
|
|
164
|
+
|
|
165
|
+
- A body of one statement on one line, which is `NEETzsche/StatementModifier`'s to rewrite.
|
|
166
|
+
- Ternaries, empty bodies, and `begin ... end while` loops.
|
|
167
|
+
- A condition that spans lines. Only the body is measured.
|
|
168
|
+
|
|
169
|
+
There is no autocorrect. The method needs a name that says what the body does, and that is a decision for a person. Moving the body by hand also means deciding what it reads and writes: a local variable the body uses becomes a parameter, one it assigns for later use becomes the return value, and a `return`, `break`, or `next` inside it means something else once it sits in another method. Once the body is one call, `NEETzsche/StatementModifier` turns the block into a modifier under `rubocop --autocorrect`.
|
|
170
|
+
|
|
171
|
+
`Style/GuardClause` and `Style/Next` may flag the same block with a different fix. Either fix satisfies this cop too, since a guard clause or a `next` leaves no block behind. Under `rubocop --autocorrect`, `Layout/LineLength` may wrap the arguments of a long modifier line and `Style/MultilineIfModifier` then restore the block form, which this cop flags. The way out is a method.
|
|
172
|
+
|
|
110
173
|
## NEETzsche/NoComments
|
|
111
174
|
|
|
112
175
|
Flags every comment. On autocorrect, a comment on its own line is deleted along with the line, and a trailing comment is deleted along with the whitespace before it.
|
|
@@ -132,6 +195,62 @@ Comments that change how Ruby or RuboCop behaves are allowed:
|
|
|
132
195
|
|
|
133
196
|
`Style/Documentation` demands the class and module comments this cop forbids, so the plugin disables it. Re-enable it in your own `.rubocop.yml` if you want the two to fight.
|
|
134
197
|
|
|
198
|
+
## NEETzsche/StatementModifier
|
|
199
|
+
|
|
200
|
+
Flags an `if`, `unless`, `while`, `until`, or `rescue` whose body is one statement on one line, and rewrites it as a statement modifier. How long the modifier line ends up does not matter.
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
# bad
|
|
204
|
+
if condition?
|
|
205
|
+
do_thing
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
until queue.empty?
|
|
209
|
+
queue.pop.call
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
begin
|
|
213
|
+
fetch
|
|
214
|
+
rescue
|
|
215
|
+
fallback
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def call
|
|
219
|
+
fetch
|
|
220
|
+
rescue StandardError
|
|
221
|
+
fallback
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# good
|
|
225
|
+
do_thing if condition?
|
|
226
|
+
queue.pop.call until queue.empty?
|
|
227
|
+
fetch rescue fallback
|
|
228
|
+
|
|
229
|
+
def call
|
|
230
|
+
fetch rescue fallback
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# left to NEETzsche/MultilineConditionalBody, because the body is more than one statement
|
|
234
|
+
if condition?
|
|
235
|
+
do_this
|
|
236
|
+
do_that
|
|
237
|
+
end
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The offense sits on the keyword. A `rescue` counts when it is the only clause, rescues `StandardError` (bare, or named, since that is what the modifier rescues), binds no variable, and has no `else`; the `begin`/`end` around it goes away, while a `def`, a block, or an `ensure` around it stays. Parentheses are added where the value is used, so `value = if condition? then compute end` becomes `value = (compute if condition?)` and `call(begin; fetch; rescue; fallback; end)` becomes `call((fetch rescue fallback))`. An assigned `rescue` needs none: `value = fetch rescue fallback` already binds that way.
|
|
241
|
+
|
|
242
|
+
Left alone, because the block form is the only correct one:
|
|
243
|
+
|
|
244
|
+
- A body of more than one statement, a statement that spans lines, or a condition that spans lines. The first two are for `NEETzsche/MultilineConditionalBody`.
|
|
245
|
+
- `else`, `elsif`, ternaries, and `begin ... end while` loops.
|
|
246
|
+
- A body that is itself an `if`, `unless`, `while`, `until`, `case`, or `rescue`, in either form. No chained modifiers.
|
|
247
|
+
- A `rescue` that names another class, lists several, binds a variable, has several clauses, an `else`, or an empty body or handler.
|
|
248
|
+
- A comment on any line of the block other than the first. A comment on the keyword line moves to the end of the modifier line.
|
|
249
|
+
- A heredoc anywhere inside.
|
|
250
|
+
- Anything whose meaning changes when the body is parsed before the condition, which is what the modifier form does: a condition that assigns a local variable, matches a pattern, or captures a named group; a body that assigns a name the condition calls as a method, as in `if name.nil?` followed by `name = default`; and an endless method definition, which would swallow the modifier.
|
|
251
|
+
|
|
252
|
+
The plugin disables `Style/IfUnlessModifier` and `Style/WhileUntilModifier`, which this cop subsumes, and `Style/RescueModifier`, which forbids what this cop demands. Re-enable them in your own `.rubocop.yml` if you want them back; `Style/IfUnlessModifier` will then fight over any modifier line longer than `Layout/LineLength` allows. `Layout/LineLength` itself is not consulted. If a modifier line is too long for it, shorten the statement or the condition, or exclude the file. Under `rubocop --autocorrect`, `Layout/LineLength` may instead wrap the arguments of a call on that line, after which `Style/MultilineIfModifier` restores the block form around the now multi-line body, and this cop leaves it there. `Style/GuardClause` and `Style/SoleNestedConditional` may flag the same block with a different fix; whichever corrects first wins, and the result satisfies both.
|
|
253
|
+
|
|
135
254
|
## Configuration
|
|
136
255
|
|
|
137
256
|
Disable one cop:
|
data/config/default.yml
CHANGED
|
@@ -9,10 +9,29 @@ NEETzsche/GeneratedMigrationTimestamp:
|
|
|
9
9
|
- "**/db/migrate/**/*.rb"
|
|
10
10
|
- "**/db/*_migrate/**/*.rb"
|
|
11
11
|
|
|
12
|
+
NEETzsche/MultilineConditionalBody:
|
|
13
|
+
Description: "Puts an `if`, `unless`, `elsif`, `else`, `while`, or `until` body that is more than one statement, or one statement over more than one line, in a method of its own."
|
|
14
|
+
Enabled: true
|
|
15
|
+
VersionAdded: "0.4.0"
|
|
16
|
+
|
|
12
17
|
NEETzsche/NoComments:
|
|
13
18
|
Description: "Forbids comments. Magic comments, shebangs, and rubocop directives are allowed."
|
|
14
19
|
Enabled: true
|
|
15
20
|
VersionAdded: "0.1.0"
|
|
16
21
|
|
|
22
|
+
NEETzsche/StatementModifier:
|
|
23
|
+
Description: "Writes an `if`, `unless`, `while`, `until`, or `rescue` whose body is one statement as a modifier, whatever the line length."
|
|
24
|
+
Enabled: true
|
|
25
|
+
VersionAdded: "0.3.0"
|
|
26
|
+
|
|
17
27
|
Style/Documentation:
|
|
18
28
|
Enabled: false
|
|
29
|
+
|
|
30
|
+
Style/IfUnlessModifier:
|
|
31
|
+
Enabled: false
|
|
32
|
+
|
|
33
|
+
Style/RescueModifier:
|
|
34
|
+
Enabled: false
|
|
35
|
+
|
|
36
|
+
Style/WhileUntilModifier:
|
|
37
|
+
Enabled: false
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module NEETzsche
|
|
6
|
+
class MultilineConditionalBody < Base
|
|
7
|
+
MSG = "Move a multi-line `%<keyword>s` body into its own method."
|
|
8
|
+
|
|
9
|
+
HEREDOC_TYPES = %i[str dstr xstr].freeze
|
|
10
|
+
|
|
11
|
+
def on_if(node)
|
|
12
|
+
return if node.ternary?
|
|
13
|
+
|
|
14
|
+
check(node.if_branch, node.loc.keyword, node.keyword)
|
|
15
|
+
check(node.else_branch, node.loc.else, "else") if node.else? && !node.elsif_conditional?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def on_while(node)
|
|
19
|
+
check(node.body, node.loc.keyword, node.keyword)
|
|
20
|
+
end
|
|
21
|
+
alias on_until on_while
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def check(body, keyword, name)
|
|
26
|
+
return if body.nil? || one_liner?(body)
|
|
27
|
+
|
|
28
|
+
add_offense(keyword, message: format(MSG, keyword: name))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def one_liner?(body)
|
|
32
|
+
!multiple_statements?(body) && body.first_line == last_line(body)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def multiple_statements?(body)
|
|
36
|
+
(body.begin_type? || body.kwbegin_type?) && body.children.size > 1
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def last_line(body)
|
|
40
|
+
heredocs = body.each_node(*HEREDOC_TYPES).select(&:heredoc?)
|
|
41
|
+
[body.last_line, *heredocs.map { |heredoc| heredoc.loc.heredoc_end.line }].max
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module NEETzsche
|
|
6
|
+
class StatementModifier < Base
|
|
7
|
+
include RangeHelp
|
|
8
|
+
extend AutoCorrector
|
|
9
|
+
|
|
10
|
+
MSG = "Write a one-statement `%<keyword>s` as a modifier."
|
|
11
|
+
|
|
12
|
+
CHAIN_TYPES = %i[if while until while_post until_post for case case_match rescue kwbegin].freeze
|
|
13
|
+
CALLS = %i[send csend super yield].freeze
|
|
14
|
+
|
|
15
|
+
def self.autocorrect_incompatible_with
|
|
16
|
+
[Style::Next, Style::SoleNestedConditional]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def on_if(node)
|
|
20
|
+
return if node.modifier_form? || node.ternary? || node.elsif? || node.else?
|
|
21
|
+
|
|
22
|
+
check_conditional(node)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def on_while(node)
|
|
26
|
+
return if node.modifier_form?
|
|
27
|
+
|
|
28
|
+
check_conditional(node)
|
|
29
|
+
end
|
|
30
|
+
alias on_until on_while
|
|
31
|
+
|
|
32
|
+
def on_rescue(node)
|
|
33
|
+
resbody = node.resbody_branches.first
|
|
34
|
+
return unless bare_rescue?(node, resbody)
|
|
35
|
+
|
|
36
|
+
target = node.parent&.kwbegin_type? ? node.parent : node
|
|
37
|
+
return unless one_liner?(target, [node.body, resbody.body])
|
|
38
|
+
|
|
39
|
+
add_offense(resbody.loc.keyword, message: format(MSG, keyword: "rescue")) do |corrector|
|
|
40
|
+
rewrite(corrector, target, rescue_statement(node, resbody), Precedence.parenthesize_rescue?(target))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def check_conditional(node)
|
|
47
|
+
return unless eligible_conditional?(node)
|
|
48
|
+
|
|
49
|
+
add_offense(node.loc.keyword, message: format(MSG, keyword: node.keyword)) do |corrector|
|
|
50
|
+
rewrite(corrector, node, conditional_statement(node), Precedence.parenthesize_conditional?(node))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def eligible_conditional?(node)
|
|
55
|
+
node.condition.single_line? && one_liner?(node, [node.body]) &&
|
|
56
|
+
!Precedence.endless_def?(node.body) && !ParseOrder.hazard?(node)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def conditional_statement(node)
|
|
60
|
+
"#{statement_source(node.body)} #{node.keyword} #{node.condition.source}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def rescue_statement(node, resbody)
|
|
64
|
+
"#{statement_source(node.body)} rescue #{statement_source(resbody.body)}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def rewrite(corrector, node, statement, parenthesize)
|
|
68
|
+
statement = "(#{statement})" if parenthesize
|
|
69
|
+
corrector.replace(node, [statement, first_line_comment(node)&.source].compact.join(" "))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def bare_rescue?(node, resbody)
|
|
73
|
+
!rescue_modifier?(resbody) && node.resbody_branches.one? && !node.else? &&
|
|
74
|
+
resbody.exception_variable.nil? && standard_error_only?(resbody.exceptions)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def rescue_modifier?(resbody)
|
|
78
|
+
processed_source.tokens.any? { |token| token.rescue_modifier? && token.pos == resbody.loc.keyword }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def standard_error_only?(exceptions)
|
|
82
|
+
return true if exceptions.empty?
|
|
83
|
+
return false unless exceptions.one? && exceptions.first.const_type?
|
|
84
|
+
|
|
85
|
+
namespace, name = *exceptions.first
|
|
86
|
+
name == :StandardError && (namespace.nil? || namespace.cbase_type?)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def one_liner?(node, statements)
|
|
90
|
+
statements.all? { |statement| single_statement?(statement) } &&
|
|
91
|
+
!modifier_body?(node) && !heredoc?(node) && comments_movable?(node)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def single_statement?(statement)
|
|
95
|
+
return false if statement.nil? || statement.begin_type? || CHAIN_TYPES.include?(statement.type)
|
|
96
|
+
return false if statement.source.end_with?(":") && !CALLS.include?(statement.type)
|
|
97
|
+
|
|
98
|
+
statement.single_line?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def modifier_body?(node)
|
|
102
|
+
parent = node.parent
|
|
103
|
+
return false unless parent && (parent.if_type? || parent.while_type? || parent.until_type?)
|
|
104
|
+
|
|
105
|
+
parent.modifier_form? && parent.body.equal?(node)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def heredoc?(node)
|
|
109
|
+
node.each_descendant(:str, :dstr, :xstr).any?(&:heredoc?)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def comments_movable?(node)
|
|
113
|
+
return false if processed_source.each_comment_in_lines((node.first_line + 1)..node.last_line).any?
|
|
114
|
+
|
|
115
|
+
first_line_comment(node).nil? || code_after(node).nil?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def first_line_comment(node)
|
|
119
|
+
processed_source.comment_at_line(node.first_line)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def code_after(node)
|
|
123
|
+
line_end = first_line_comment(node)&.source_range&.begin_pos if node.single_line?
|
|
124
|
+
line_end ||= processed_source.buffer.line_range(node.last_line).end_pos
|
|
125
|
+
code = range_between(node.source_range.end_pos, line_end).source.strip
|
|
126
|
+
code unless code.empty?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def statement_source(statement)
|
|
130
|
+
return statement.source unless statement.source.end_with?(":")
|
|
131
|
+
|
|
132
|
+
head = range_between(statement.source_range.begin_pos, statement.first_argument.source_range.begin_pos)
|
|
133
|
+
"#{head.source.rstrip}(#{statement.arguments.map(&:source).join(", ")})"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
module ParseOrder
|
|
137
|
+
CAPTURING_CONDITIONS = %i[lvasgn match_with_lvasgn match_pattern match_pattern_p].freeze
|
|
138
|
+
|
|
139
|
+
module_function
|
|
140
|
+
|
|
141
|
+
def hazard?(node)
|
|
142
|
+
condition = node.condition
|
|
143
|
+
return true if condition.each_node(*CAPTURING_CONDITIONS).any?
|
|
144
|
+
|
|
145
|
+
assigned = node.body.each_node(:lvasgn).map { |assignment| assignment.children.first }
|
|
146
|
+
return false if assigned.empty?
|
|
147
|
+
|
|
148
|
+
condition.each_node(:send).any? { |call| bare_identifier?(call) && assigned.include?(call.method_name) }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def bare_identifier?(call)
|
|
152
|
+
call.receiver.nil? && call.arguments.empty? && !call.parenthesized? && call.block_node.nil?
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
module Precedence
|
|
157
|
+
STATEMENT_PARENTS = %i[begin kwbegin def defs block numblock itblock class module sclass
|
|
158
|
+
ensure rescue resbody preexe postexe].freeze
|
|
159
|
+
ASSIGNMENTS = %i[lvasgn ivasgn cvasgn gvasgn casgn masgn op_asgn or_asgn and_asgn indexasgn].freeze
|
|
160
|
+
|
|
161
|
+
module_function
|
|
162
|
+
|
|
163
|
+
def parenthesize_conditional?(node)
|
|
164
|
+
!statement_position?(node)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def parenthesize_rescue?(node)
|
|
168
|
+
!statement_position?(node) && !assignment_value?(node)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def endless_def?(body)
|
|
172
|
+
%i[def defs].include?(body.type) && body.endless?
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def statement_position?(node)
|
|
176
|
+
parent = node.parent
|
|
177
|
+
return true if parent.nil? || STATEMENT_PARENTS.include?(parent.type)
|
|
178
|
+
|
|
179
|
+
case parent.type
|
|
180
|
+
when :if, :while, :until, :case, :case_match then !parent.children.first.equal?(node)
|
|
181
|
+
when :for, :when, :in_pattern then parent.body.equal?(node)
|
|
182
|
+
else false
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def assignment_value?(node)
|
|
187
|
+
parent = node.parent
|
|
188
|
+
return false unless parent.children.last.equal?(node)
|
|
189
|
+
|
|
190
|
+
ASSIGNMENTS.include?(parent.type) || (parent.send_type? && parent.assignment_method?)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: officer_neetzsche
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- TheStranjer
|
|
@@ -52,7 +52,9 @@ files:
|
|
|
52
52
|
- lib/officer_neetzsche/plugin.rb
|
|
53
53
|
- lib/officer_neetzsche/version.rb
|
|
54
54
|
- lib/rubocop/cop/neetzsche/generated_migration_timestamp.rb
|
|
55
|
+
- lib/rubocop/cop/neetzsche/multiline_conditional_body.rb
|
|
55
56
|
- lib/rubocop/cop/neetzsche/no_comments.rb
|
|
57
|
+
- lib/rubocop/cop/neetzsche/statement_modifier.rb
|
|
56
58
|
- lib/rubocop/cop/neetzsche_cops.rb
|
|
57
59
|
homepage: https://github.com/TheStranjer/OfficerNEETzsche
|
|
58
60
|
licenses: []
|