runger_style 5.17.0 → 5.18.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: eb6defe797f70ab9b1335060521e1f49e4cf509c30ac7bf3139e04a0a3e8cae5
4
- data.tar.gz: 95c757a383b153f087aaff78b4514043a9029b491e2fec26942991fb318d8331
3
+ metadata.gz: d8102a9e67c0aacf7a402f6a985b0994bade06372295bb168600812755503156
4
+ data.tar.gz: 68ae43f161e34dfdccc6bce2b35d151782088911b3aa7494e4e467d0c8396de4
5
5
  SHA512:
6
- metadata.gz: 04c29fd64211c7dca6756bfe470a6ce0b0fd5d3f584f0ed437d3410771cd16e1f5f8bf25804d9c1483af833065e2290ff04d185071832be10c0efaf174dad972
7
- data.tar.gz: cc4ed34f1d5a1366305c9e3b8018dd109e3f80d556980db9c10721fbea985e47d5b47ebb2d1576b53d2c113089a579ab58b95f219f9dc151d43ece979986886d
6
+ metadata.gz: f46b144fba27601f8cefc14c210534ca8df8c8da8530e2da753d6ee948369558311f6d8de39a72e2d4f0ef995ad458f5fb2f028f9b1db4954444514c9abce6b1
7
+ data.tar.gz: 48c8b7c8e7831fa2932511c0a83b6015f44374afb8cea158594c276c5470e65e97e10a3ac002ed16f90229333990f5f87bf5fe20891778feadad415a6c29eb55
data/CHANGELOG.md CHANGED
@@ -1,6 +1,9 @@
1
1
  ## Unreleased
2
2
  [no unreleased changes yet]
3
3
 
4
+ ## v5.18.0 (2026-08-22)
5
+ - [default] Add `RungerStyle/ParenthesesAroundMultilineCondition` to require parentheses around multiline `if`, `unless`, and `elsif` conditions, except for multiline block conditions and parenthesized receivers.
6
+
4
7
  ## v5.17.0 (2026-08-21)
5
8
  - [default] Add report-only `RungerStyle/NoReturn` to forbid explicit `return` statements.
6
9
 
data/Gemfile.lock CHANGED
@@ -10,7 +10,7 @@ GIT
10
10
  PATH
11
11
  remote: .
12
12
  specs:
13
- runger_style (5.17.0)
13
+ runger_style (5.18.0)
14
14
  prism (>= 0.24.0)
15
15
  rubocop (>= 1.72.0)
16
16
 
@@ -235,7 +235,7 @@ CHECKSUMS
235
235
  ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
236
236
  runger_byebug (11.4.0) sha256=569e22ba2215f57e7f69e145fcb63be401e29fcd312f7936d813e12d0c7bbee6
237
237
  runger_release_assistant (4.3.1) sha256=3246c925240739eb12b9e6b9526a77ea00fcf0a0941441ff5ffacefb7e3e4d83
238
- runger_style (5.17.0)
238
+ runger_style (5.18.0)
239
239
  securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
240
240
  slop (4.10.1) sha256=844322b5ffcf17ed4815fdb173b04a20dd82b4fd93e3744c88c8fafea696d9c7
241
241
  tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
@@ -12,9 +12,11 @@ module RungerStyle # rubocop:disable Style/ClassAndModuleChildren
12
12
  # When a method call uses keyword arguments without braces,
13
13
  # the parser produces a single hash node. In that case, inspect its pairs.
14
14
  arguments =
15
- if node.arguments.one? &&
15
+ if (
16
+ node.arguments.one? &&
16
17
  node.arguments.first.hash_type? &&
17
18
  !node.arguments.first.braces?
19
+ )
18
20
  node.arguments.first.pairs
19
21
  else
20
22
  node.arguments
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RungerStyle # rubocop:disable Style/ClassAndModuleChildren
4
+ class ParenthesesAroundMultilineCondition < ::RuboCop::Cop::Base
5
+ extend ::RuboCop::Cop::AutoCorrector
6
+ include ::RuboCop::Cop::RangeHelp
7
+
8
+ MSG = 'Wrap multiline `%<keyword>s` conditions in parentheses.'
9
+
10
+ def on_if(node)
11
+ if offense?(node)
12
+ add_offense(node.loc.keyword, message: format(MSG, keyword: node.keyword)) do |corrector|
13
+ autocorrect(corrector, node)
14
+ end
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def offense?(node)
21
+ multiline_conditional?(node) &&
22
+ !permitted_multiline_condition?(node) &&
23
+ !correctly_parenthesized?(node)
24
+ end
25
+
26
+ def correctly_parenthesized?(node)
27
+ inner_condition = parenthesized_condition(node.condition)
28
+
29
+ if inner_condition
30
+ condition_starts_on_next_line?(node, inner_condition) &&
31
+ closing_parenthesis_on_own_line?(node.condition, inner_condition)
32
+ end
33
+ end
34
+
35
+ def multiline_conditional?(node)
36
+ !node.modifier_form? && !node.ternary? && node.condition.multiline?
37
+ end
38
+
39
+ def permitted_multiline_condition?(node)
40
+ condition = node.condition
41
+
42
+ condition.any_block_type? || multiline_parenthesized_receiver?(condition)
43
+ end
44
+
45
+ def multiline_parenthesized_receiver?(condition)
46
+ if condition.send_type? || condition.csend_type?
47
+ receiver = condition.receiver
48
+ receiver&.begin_type? && receiver.multiline?
49
+ end
50
+ end
51
+
52
+ def autocorrect(corrector, node)
53
+ condition = node.condition
54
+ inner_condition = parenthesized_condition(condition)
55
+ condition_start =
56
+ if inner_condition
57
+ inner_condition.source_range.begin_pos
58
+ else
59
+ condition.source_range.begin_pos
60
+ end
61
+
62
+ if inner_condition
63
+ if !condition_starts_on_next_line?(node, inner_condition)
64
+ corrector.replace(
65
+ range_between(node.loc.keyword.end_pos, condition_start),
66
+ " (\n#{condition_indentation(node)}",
67
+ )
68
+ end
69
+
70
+ if !closing_parenthesis_on_own_line?(condition, inner_condition)
71
+ corrector.replace(
72
+ range_between(inner_condition.source_range.end_pos, condition.loc.end.end_pos),
73
+ "\n#{base_indentation(node)})",
74
+ )
75
+ end
76
+ else
77
+ corrector.replace(
78
+ range_between(node.loc.keyword.end_pos, condition_start),
79
+ " (\n#{condition_indentation(node)}",
80
+ )
81
+
82
+ condition_end = condition_end_position(condition)
83
+ corrector.insert_after(
84
+ range_between(condition_end, condition_end),
85
+ "\n#{base_indentation(node)})",
86
+ )
87
+ end
88
+ end
89
+
90
+ def condition_starts_on_next_line?(node, inner_condition)
91
+ inner_condition.first_line > node.loc.keyword.line
92
+ end
93
+
94
+ def closing_parenthesis_on_own_line?(condition, inner_condition)
95
+ condition.loc.end.line > inner_condition.source_range.last_line
96
+ end
97
+
98
+ def parenthesized_condition(condition)
99
+ if (
100
+ condition.begin_type? &&
101
+ condition.children.one? &&
102
+ condition.loc.begin &&
103
+ condition.loc.end
104
+ )
105
+ condition.children.first
106
+ end
107
+ end
108
+
109
+ def condition_end_position(condition)
110
+ end_position = condition.source_range.end_pos
111
+ last_line = condition.source_range.last_line
112
+
113
+ processed_source.comments.each do |comment|
114
+ if comment.location.line == last_line && comment.source_range.begin_pos >= end_position
115
+ end_position = comment.source_range.end_pos
116
+ end
117
+ end
118
+
119
+ end_position
120
+ end
121
+
122
+ def base_indentation(node)
123
+ node.loc.keyword.source_line[/\A[ \t]*/]
124
+ end
125
+
126
+ def condition_indentation(node)
127
+ base_indentation(node) + (' ' * indentation_width)
128
+ end
129
+
130
+ def indentation_width
131
+ config.for_cop('Layout/IndentationWidth')['Width'] || 2
132
+ end
133
+ end
134
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RungerStyle
4
- VERSION = '5.17.0'
4
+ VERSION = '5.18.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: runger_style
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.17.0
4
+ version: 5.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Runger
@@ -77,6 +77,7 @@ files:
77
77
  - lib/runger_style/cops/default/multiline_hash_value_indentation.rb
78
78
  - lib/runger_style/cops/default/multiline_method_arguments_line_breaks.rb
79
79
  - lib/runger_style/cops/default/no_return.rb
80
+ - lib/runger_style/cops/default/parentheses_around_multiline_condition.rb
80
81
  - lib/runger_style/cops/default/require_all_custom_cops.rb
81
82
  - lib/runger_style/cops/default/require_all_monkeypatches.rb
82
83
  - lib/runger_style/version.rb