Skip to content

Commit eb89bdd

Browse files
authored
Merge pull request #1292 from fatkodima/fix-where-range-complex-expressions
[Fix #1281] Fix `WhereRange` autocorrect for complex expressions
2 parents 523fed1 + 6bb23b4 commit eb89bdd

File tree

3 files changed

+81
-36
lines changed

3 files changed

+81
-36
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#1281](https://github.com/rubocop/rubocop-rails/issues/1281): Fix `WhereRange` autocorrect for complex expressions. ([@fatkodima][])

lib/rubocop/cop/rails/where_range.rb

Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -86,47 +86,63 @@ def where_not?(node)
8686

8787
# rubocop:disable Metrics
8888
def extract_column_and_value(template_node, values_node)
89-
value =
90-
case template_node.value
91-
when GTEQ_ANONYMOUS_RE
92-
"#{values_node[0].source}.."
93-
when LTEQ_ANONYMOUS_RE
94-
range_operator = range_operator(Regexp.last_match(2))
95-
"#{range_operator}#{values_node[0].source}" if target_ruby_version >= 2.7
96-
when RANGE_ANONYMOUS_RE
97-
if values_node.size >= 2
98-
range_operator = range_operator(Regexp.last_match(2))
99-
"#{values_node[0].source}#{range_operator}#{values_node[1].source}"
100-
end
101-
when GTEQ_NAMED_RE
102-
value_node = values_node[0]
89+
case template_node.value
90+
when GTEQ_ANONYMOUS_RE
91+
lhs = values_node[0]
92+
operator = '..'
93+
when LTEQ_ANONYMOUS_RE
94+
if target_ruby_version >= 2.7
95+
operator = range_operator(Regexp.last_match(2))
96+
rhs = values_node[0]
97+
end
98+
when RANGE_ANONYMOUS_RE
99+
if values_node.size >= 2
100+
lhs = values_node[0]
101+
operator = range_operator(Regexp.last_match(2))
102+
rhs = values_node[1]
103+
end
104+
when GTEQ_NAMED_RE
105+
value_node = values_node[0]
103106

104-
if value_node.hash_type?
105-
pair = find_pair(value_node, Regexp.last_match(2))
106-
"#{pair.value.source}.." if pair
107-
end
108-
when LTEQ_NAMED_RE
109-
value_node = values_node[0]
110-
111-
if value_node.hash_type?
112-
pair = find_pair(value_node, Regexp.last_match(2))
113-
if pair && target_ruby_version >= 2.7
114-
range_operator = range_operator(Regexp.last_match(2))
115-
"#{range_operator}#{pair.value.source}"
116-
end
107+
if value_node.hash_type?
108+
pair = find_pair(value_node, Regexp.last_match(2))
109+
lhs = pair.value
110+
operator = '..'
111+
end
112+
when LTEQ_NAMED_RE
113+
value_node = values_node[0]
114+
115+
if value_node.hash_type?
116+
pair = find_pair(value_node, Regexp.last_match(2))
117+
if pair && target_ruby_version >= 2.7
118+
operator = range_operator(Regexp.last_match(2))
119+
rhs = pair.value
117120
end
118-
when RANGE_NAMED_RE
119-
value_node = values_node[0]
120-
121-
if value_node.hash_type?
122-
range_operator = range_operator(Regexp.last_match(3))
123-
pair1 = find_pair(value_node, Regexp.last_match(2))
124-
pair2 = find_pair(value_node, Regexp.last_match(4))
125-
"#{pair1.value.source}#{range_operator}#{pair2.value.source}" if pair1 && pair2
121+
end
122+
when RANGE_NAMED_RE
123+
value_node = values_node[0]
124+
125+
if value_node.hash_type?
126+
pair1 = find_pair(value_node, Regexp.last_match(2))
127+
pair2 = find_pair(value_node, Regexp.last_match(4))
128+
129+
if pair1 && pair2
130+
lhs = pair1.value
131+
operator = range_operator(Regexp.last_match(3))
132+
rhs = pair2.value
126133
end
127134
end
135+
end
128136

129-
[Regexp.last_match(1), value] if value
137+
if lhs
138+
lhs_source = parentheses_needed?(lhs) ? "(#{lhs.source})" : lhs.source
139+
end
140+
141+
if rhs
142+
rhs_source = parentheses_needed?(rhs) ? "(#{rhs.source})" : rhs.source
143+
end
144+
145+
[Regexp.last_match(1), "#{lhs_source}#{operator}#{rhs_source}"] if operator
130146
end
131147
# rubocop:enable Metrics
132148

@@ -151,6 +167,23 @@ def build_good_method(method_name, column, value)
151167
"#{method_name}(#{column}: #{value})"
152168
end
153169
end
170+
171+
def parentheses_needed?(node)
172+
!parentheses_not_needed?(node)
173+
end
174+
175+
def parentheses_not_needed?(node)
176+
node.variable? ||
177+
node.literal? ||
178+
node.reference? ||
179+
node.const_type? ||
180+
node.begin_type? ||
181+
parenthesized_call_node?(node)
182+
end
183+
184+
def parenthesized_call_node?(node)
185+
node.call_type? && (node.arguments.empty? || node.parenthesized_call?)
186+
end
154187
end
155188
end
156189
end

spec/rubocop/cop/rails/where_range_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,17 @@
172172
foo.not('column >= ?', value)
173173
RUBY
174174
end
175+
176+
it 'wraps complex expressions by parentheses' do
177+
expect_offense(<<~RUBY)
178+
Model.where('column >= ?', true ? 1 : 2)
179+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `where(column: (true ? 1 : 2)..)` instead of manually constructing SQL.
180+
RUBY
181+
182+
expect_correction(<<~RUBY)
183+
Model.where(column: (true ? 1 : 2)..)
184+
RUBY
185+
end
175186
end
176187

177188
context 'Ruby >= 2.6', :ruby26, unsupported_on: :prism do

0 commit comments

Comments
 (0)