lib/rubocop/cop/style/parentheses_around_condition.rb
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# Checks for the presence of superfluous parentheses around the
# condition of if/unless/while/until.
#
# `AllowSafeAssignment` option for safe assignment.
# By safe assignment we mean putting parentheses around
# an assignment to indicate "I know I'm using an assignment
# as a condition. It's not a mistake."
#
# @example
# # bad
# x += 1 while (x < 10)
# foo unless (bar || baz)
#
# if (x > 10)
# elsif (x < 3)
# end
#
# # good
# x += 1 while x < 10
# foo unless bar || baz
#
# if x > 10
# elsif x < 3
# end
#
# @example AllowSafeAssignment: true (default)
# # good
# foo unless (bar = baz)
#
# @example AllowSafeAssignment: false
# # bad
# foo unless (bar = baz)
#
# @example AllowInMultilineConditions: false (default)
# # bad
# if (x > 10 &&
# y > 10)
# end
#
# # good
# if x > 10 &&
# y > 10
# end
#
# @example AllowInMultilineConditions: true
# # good
# if (x > 10 &&
# y > 10)
# end
#
class ParenthesesAroundCondition < Base
include SafeAssignment
include Parentheses
include RangeHelp
extend AutoCorrector
def on_if(node)
return if node.ternary?
process_control_op(node)
end
def on_while(node)
process_control_op(node)
end
alias on_until on_while
private
# @!method control_op_condition(node)
def_node_matcher :control_op_condition, <<~PATTERN
(begin $_ $...)
PATTERN
def process_control_op(node)
cond = node.condition
control_op_condition(cond) do |first_child, rest_children|
return if require_parentheses?(node, first_child)
return if semicolon_separated_expressions?(first_child, rest_children)
return if modifier_op?(first_child)
return if parens_allowed?(cond)
message = message(cond)
add_offense(cond, message: message) do |corrector|
ParenthesesCorrector.correct(corrector, cond)
end
end
end
def require_parentheses?(node, condition_body)
return false if !node.while_type? && !node.until_type?
return false if !condition_body.block_type? && !condition_body.numblock_type?
condition_body.send_node.block_literal? && condition_body.keywords?
end
def semicolon_separated_expressions?(first_exp, rest_exps)
return false unless (second_exp = rest_exps.first)
range = range_between(first_exp.source_range.end_pos, second_exp.source_range.begin_pos)
range.source.include?(';')
end
def modifier_op?(node)
return false if node.if_type? && node.ternary?
return true if node.rescue_type?
node.basic_conditional? && node.modifier_form?
end
def message(node)
kw = node.parent.keyword
article = kw == 'while' ? 'a' : 'an'
"Don't use parentheses around the condition of #{article} `#{kw}`."
end
def parens_allowed?(node)
parens_required?(node) ||
(safe_assignment?(node) && safe_assignment_allowed?) ||
(node.multiline? && allow_multiline_conditions?)
end
def allow_multiline_conditions?
cop_config['AllowInMultilineConditions']
end
end
end
end
end